GithubHelp home page GithubHelp logo

aliyun-oss-php-sdk's Introduction

Alibaba Cloud OSS SDK for PHP

Latest Stable Version Build Status Coverage Status

Overview

Alibaba Cloud Object Storage Service (OSS) is a cloud storage service provided by Alibaba Cloud, featuring a massive capacity, security, a low cost, and high reliability. You can upload and download data on any application anytime and anywhere by calling APIs, and perform simple management of data through the web console. The OSS can store any type of files and therefore applies to various websites, development enterprises and developers.

Run environment

  • PHP 5.3+.
  • cURL extension.

Tips:

  • In Ubuntu, you can use the apt-get package manager to install the PHP cURL extension: sudo apt-get install php5-curl.

Install OSS PHP SDK

  • If you use the composer to manage project dependencies, run the following command in your project's root directory:

      composer require aliyuncs/oss-sdk-php
    

    You can also declare the dependency on Alibaba Cloud OSS SDK for PHP in the composer.json file.

      "require": {
          "aliyuncs/oss-sdk-php": "~2.0"
      }
    

    Then run composer install to install the dependency. After the Composer Dependency Manager is installed, import the dependency in your PHP code:

      require_once __DIR__ . '/vendor/autoload.php';
    
  • You can also directly download the packaged PHAR File, and introduce the file to your code:

      require_once '/path/to/oss-sdk-php.phar';
    
  • Download the SDK source code, and introduce the autoload.php file under the SDK directory to your code:

      require_once '/path/to/oss-sdk/autoload.php';
    

Quick use

Common classes

Class Explanation
OSS\OssClient OSS client class. An OssClient instance can be used to call the interface.
OSS\Core\OssException OSS Exception class . You only need to pay attention to this exception when you use the OssClient.

Initialize an OssClient

The SDK's operations for the OSS are performed through the OssClient class. The code below creates an OssClient object:

<?php
$accessKeyId = "<AccessKeyID that you obtain from OSS>";
$accessKeySecret = "<AccessKeySecret that you obtain from OSS>";
$endpoint = "<Domain that you select to access an OSS data center, such as "oss-cn-hangzhou.aliyuncs.com>";
try {
    $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
} catch (OssException $e) {
    print $e->getMessage();
}

Operations on objects

Objects are the most basic data units on the OSS. You can simply consider objects as files. The following code uploads an object:

<?php
$bucket= "<Name of the bucket in use. Pay attention to naming conventions>";
$object = "<Name of the object in use. Pay attention to naming conventions>";
$content = "Hello, OSS!"; // Content of the uploaded file
try {
    $ossClient->putObject($bucket, $object, $content);
} catch (OssException $e) {
    print $e->getMessage();
}

Operations on buckets

Buckets are the space that you use to manage the stored objects. It is an object management unit for users. Each object must belong to a bucket. You can create a bucket with the following code:

<?php
$bucket= "<Name of the bucket in use. Pay attention to naming conventions>";
try {
    $ossClient->createBucket($bucket);
} catch (OssException $e) {
    print $e->getMessage();
}

Handle returned results

The OssClient provides the following two types of returned data from interfaces:

  • Put and Delete interfaces: The PUT and DELETE operations are deemed successful if null is returned by the interfaces without OSSException.

  • Get and List interfaces: The GET and LIST operations are deemed successful if the desired data is returned by the interfaces without OSSException. For example,

    <?php
    $bucketListInfo = $ossClient->listBuckets();
    $bucketList = $bucketListInfo->getBucketList();
    foreach($bucketList as $bucket) {
        print($bucket->getLocation() . "\t" . $bucket->getName() . "\t" . $bucket->getCreateDate() . "\n");
    }

In the above code, $bucketListInfo falls into the 'OSS\Model\BucketListInfo' data type.

Run a sample project

  • Modify samples/Config.php to complete the configuration information.
  • Run cd samples/ && php RunAll.php.

Run a unit test

  • Run composer install to download the dependent libraries.

  • Set the environment variable.

      export OSS_ACCESS_KEY_ID=access-key-id
      export OSS_ACCESS_KEY_SECRET=access-key-secret
      export OSS_ENDPOINT=endpoint
      export OSS_BUCKET=bucket-name
    
  • Run php vendor/bin/phpunit

License

  • MIT

Contact us

aliyun-oss-php-sdk's People

Contributors

baiyubin2020 avatar hefei1986 avatar huiguangjun avatar itxiao6 avatar khs1994 avatar ljbreak2008 avatar popeng007 avatar qiyuewuyi avatar robertyue19900425 avatar rockuw avatar taobig avatar wvfeng avatar xiaozhuai avatar xuxucode avatar yangzong18 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

aliyun-oss-php-sdk's Issues

Unable to read files with no extension

I am unable to read a file with no extension using this SDK.
$result = $client->getObject("my-bucket", "dir/noext");
this line returns "Can you read it?"

Any solution?

PHP SDK: 分片上传后,Etag, Content-MD5 错误

分片上传后,Etag, Content-MD5 错误。

文件使用multiuploadFile分片上传,当文件大小超过 OssUtil::OSS_MID_PART_SIZE或者设置分片大小,进行上传, 完成上传后, 使用getObjectMeta,
etag出现带编号的字符,类似""DBBCA06B72052598E18B038678236B8B-5"",
Content-MD5值为空不存在。

错误似乎发生:completeMultipartUpload
对象的ETag变成了这个请求中XML加密后生成的ETag。
这里似乎缺少一步,即设置整个文件对象的ETag以及Content-MD5。

https://help.aliyun.com/document_detail/31995.html?spm=5176.doc31947.6.882.HiU2Cu 在这个链接中返回示例中返回也是类似的ETag。

但是如果按照这个逻辑,整个OSS通过分片上传产生的ETag以及Content-MD5都会有问题,那其它用户是如何取得正确的Content-MD5的?

2017年1月3日 18:19 更新
刚才对WEB端JS上传进行测试,如果进行分片上传,该对象的ETag及Content-MD5同样会使用最后一片上传内容的ETag和Content-MD5。

那如果假定所有的Content-MD5不可用,如何使用SDK取得某一个对象的正确的Content-MD5值?

能不能出个简单版的,不用java的模式来写php

由于php是运行时加载源文件,他的运行机制和其他语音由本质区别,php层面应该尽量轻薄,这么简单个东西,每一次访问都得加载这么一大堆零碎的文件,执行效率和开发效率都很低

uploadfile没法自定义content-type

public function uploadFile($bucket, $object, $file, $options = NULL)
{
    $this->precheckCommon($bucket, $object, $options);
    OssUtil::throwOssExceptionWithMessageIfEmpty($file, "file path is invalid");
    $file = OssUtil::encodePath($file);
    if (!file_exists($file)) {
        throw new OssException($file . " file does not exist");
    }
    $options[self::OSS_FILE_UPLOAD] = $file;
    $file_size = filesize($options[self::OSS_FILE_UPLOAD]);
    $is_check_md5 = $this->isCheckMD5($options);
    if ($is_check_md5) {
        $content_md5 = base64_encode(md5_file($options[self::OSS_FILE_UPLOAD], true));
        $options[self::OSS_CONTENT_MD5] = $content_md5;
    }
    $content_type = $this->getMimeType($file);
    $options[self::OSS_METHOD] = self::OSS_HTTP_PUT;
    $options[self::OSS_BUCKET] = $bucket;
    $options[self::OSS_OBJECT] = $object;
    $options[self::OSS_CONTENT_TYPE] = $content_type;
    $options[self::OSS_CONTENT_LENGTH] = $file_size;
    $response = $this->auth($options);
    $result = new PutSetDeleteResult($response);
    return $result->getData();
    }

content-type是根据$file取的,但是上传文件的时候很多时候没有后缀名的,所以这里取到的并不正确,是不是应该如果options里定义了content-type就不要覆盖。

上传文件时,不能设置header, OSS_CONTENT_DISPOSTION

上传文件时,不能设置header, OSS_CONTENT_DISPOSTION,导制不能下载是按上传文件时不能按 OSS_CONTENT_DISPOSTION描述的文件名下载

OssClient.php
private function generateHeaders($options, $hostname)

$headers = array(
self::OSS_CONTENT_MD5 => '',
self::OSS_CONTENT_TYPE => isset($options[self::OSS_CONTENT_TYPE]) ? $options[self::OSS_CONTENT_TYPE] : self::DEFAULT_CONTENT_TYPE,
self::OSS_DATE => isset($options[self::OSS_DATE]) ? $options[self::OSS_DATE] : gmdate('D, d M Y H:i:s \G\M\T'),
self::OSS_HOST => $hostname,
self::OSS_CONTENT_DISPOSTION => isset($options[self::OSS_CONTENT_DISPOSTION]) ? $options[self::OSS_CONTENT_DISPOSTION] : '',
);
#1917
加上这个
self::OSS_CONTENT_DISPOSTION => isset($options[self::OSS_CONTENT_DISPOSTION]) ? $options[self::OSS_CONTENT_DISPOSTION] : '',

OssClient->uploadFile()的时候出现警告(已解决)

private function authPrecheckObjectEncoding(&$options)
{
$tmp_object = $options[self::OSS_OBJECT];
try {
if (OssUtil::isGb2312($options[self::OSS_OBJECT])) {
$options[self::OSS_OBJECT] = iconv('GB2312', "UTF-8//IGNORE", $options[self::OSS_OBJECT]);
} elseif (OssUtil::checkChar($options[self::OSS_OBJECT], true)) {
$options[self::OSS_OBJECT] = iconv('GBK', "UTF-8//IGNORE", $options[self::OSS_OBJECT]);
}
} catch (\Exception $e) { //这里没有捕捉到这个异常
try {
$tmp_object = iconv(mb_detect_encoding($tmp_object), "UTF-8", $tmp_object);
} catch (\Exception $e) {
}
}
$options[self::OSS_OBJECT] = $tmp_object;
}

Notice: iconv(): Wrong charset, conversion from GBK' to UTF-8//IGNORE' is not allowed in
/var/www/html/vendor/aliyuncs/oss-sdk-php/src/OSS/OssClient.php on line
2301

如何直接将分片上传(即假定当前已分片好),然后对所有分片进行合并.

你好,我在sdk开发遇到了一个问题,来请教一下工程师.

已实现的传到本地服务器流程:1前端将文件分片=2传至服务器=3最后一片完成

传至OSS:2和3是如何用SDK里的分片上传进行实现,我看文档里都是用本地的整个文件进行分片然后再每片上传

但我是已经由前端分片好了,目标是将前端发送的一片片传至OSS,这样不知道怎么用SDK实现.

Translating into English

Hi

This sdk document is Chinese.
But we Japanese also use this library.
Will you tranlate it into English?
If I request PR of translating into English, do you merge?

centos服务器,php上传碰到问题: cURL resource: Resource id #58; cURL error: select/poll returned error (55)

之前都是ok 的 ,最近突然发生这个问题,影响了我所有的上传。

RequestCoreException: cURL resource: Resource id #58; cURL error: select/poll returned error (55)
错误位置
FILE: /usr/share/nginx/html/ThinkPHP/Library/Vendor/Alioss/src/OSS/OssClient.php  LINE: 1641
TRACE
#0 /usr/share/nginx/html/ThinkPHP/Library/Vendor/Alioss/src/OSS/OssClient.php(782): OSS\OssClient->auth(Array)
#1 /usr/share/nginx/html/Application/Common/Common/function.php(1088):


调用OssClient->putObject方法,如果$object中含有百分号就会出问题

调用OssClient->putObject方法,
如果$object中含有百分号就会抛出一个空的异常,内容如下
“: RequestId:”

测试参数
putObject($config, 'test/%.txt', 'test content');

从源码中获取了下response

body:

    <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\r\n
    <html>\r\n
    <head><title>400 Bad Request</title></head>\r\n
    <body bgcolor="white">\r\n
    <h1>400 Bad Request</h1>\r\n
    <p>Your browser sent a request that this server could not understand.<hr/>Powered by AliyunOSS</body>\r\n
    </html>\r\n

uploadDir() 子文件夾上傳問題

Hello,
我在用 uploadDir() 去上傳文件夾,但好像子文件夾命名有問題。

the code:

  $exclude = '.|..|.svn|.git';
  $recursive = TRUE;
  $checkMd5 = TRUE;
  $result = $client->uploadDir($bucket, $dest, $src, $exclude, $recursive, $checkMd5);

folder struct

  test (folder)
    - abc.txt (file)
    - def.txt (file)
    - test_sub (folder)
      - ghj.txt (file)

expected result should be

Array
(
    [succeededList] => Array
        (
            [0] => test/test_sub/ghj.txt
            [1] => test/abc.txt
            [2] => test/def.txt
        )

    [failedList] => Array
        (
        )

)

but now it returns

Array
(
    [succeededList] => Array
        (
            [0] => test/_sub/ghj.txt
            [1] => test/abc.txt
            [2] => test/def.txt
        )

    [failedList] => Array
        (
        )

)

似乎是 OSS\Core\OssUtil::readDir() 第 416 行會 replace 走個 $base_path?

PHP 7 下内存占用不释放

测试环境

  • ubuntu 14.04
  • PHP 7.0.0-2+deb.sury.org~trusty+1 (cli) ( NTS )

php 5.5.30 下测试正常

测试代码

$config = [
    'access_key_id' => 'ACCESS_KEY_ID',
    'access_key_secret' => 'ACCESS_KEY_SECRET',
    'endpoint' => 'http://oss-cn-beijing.aliyuncs.com',
    'bucket' => 'BUCKET',
];
$ossClient = new OssClient($config['access_key_id'], $config['access_key_secret'], $config['endpoint']);
for ($i=0;$i<100;$i++) {
    $object = 'testfile';
    print sprintf('%.2f M', memory_get_usage() / 1024 / 1024) . "\n";
    $ossClient->doesObjectExist($config['bucket'], $object);
}

输出结果

7.98 M
8.10 M
8.10 M
8.11 M
8.12 M
8.13 M
8.14 M
8.14 M
8.15 M
8.16 M
8.17 M
8.18 M
8.19 M
8.19 M
8.20 M
8.21 M
8.22 M
8.23 M

iconv(): Wrong charset, conversion from `GBK' to `UTF-8//IGNORE' is not allowed in /app/vendor/aliyuncs/oss-sdk-php/src/OSS/OssClient.php on line 2301

bash-4.3# php -v
PHP 7.1.9 (cli) (built: Oct  2 2017 20:51:54) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
    with Zend OPcache v7.1.9, Copyright (c) 1999-2017, by Zend Technologies
    with Xdebug v2.5.3, Copyright (c) 2002-2017, by Derick Rethans
bash-4.3# php -m
[PHP Modules]
apcu
bcmath
bz2
calendar
Core
ctype
curl
date
dom
exif
fileinfo
filter
ftp
gd
gettext
hash
iconv
intl
json
ldap
libxml
mbstring
mcrypt
mysqli
mysqlnd
openssl
pcntl
pcre
PDO
pdo_mysql
pdo_sqlite
Phar
posix
readline
Reflection
session
shmop
SimpleXML
soap
sockets
SPL
sqlite3
standard
sysvmsg
sysvsem
sysvshm
tokenizer
wddx
xdebug
xml
xmlreader
xmlrpc
xmlwriter
xsl
Zend OPcache
zip
zlib

[Zend Modules]
Xdebug
Zend OPcache

creatbucket时报错了

The request signature we calculated does not match the signature you provided. Check your key and signing method.

这是什么情况下才会出现啊?我测试了一下不存在的bucket才会报这个错呢。。

oss生成签字的时间是哪个时区?

现在说下遇到的问题,我程序里面使用的是 UCT+0时区,调用ossAPI就是失败。生成签字没问题,应该是时区不一样造成阿里云验证签字失败。

2.2.1的phar包在5.3.29下引入有问题?

因为项目用的yii1的版本 所以图方便直接下了个phar来用 在本地php7下测试通过

但是线上环境是5.3.29 一引入文件就爆出两个?? 然后代码执行就断了..请问有人遇到过没有..

PHP7.1.0不兼容问题

在公司的测试服务器上,php版本是7.1.0。sdk里面代码很多不兼容。
$response = $this->auth($options); 代码里面的依赖报错了,改了几个问题(iconv函数)还是有错误,到后面都没办法调试了.希望官方fix下!

OSS\Core\OssException' with message 'RequestCoreException: cURL resource: Resource id #12; cURL error: Empty reply from server

三天两头出现这个问题,什么情况啊。。。。
有时候,单独拿在测试用,同一个文件,调用方法也就那几行,死活上不去,改个远程文件名就上传成功!一次上传成功过后,继续上传按照原来的文件、原来的逻辑、原来的远程文件名,就又可以上传了。
OSS和MNS两个写的乱七八糟的,MNS那个自从上周出了事故后,天天报错。还没有反馈入口(MNS那个一天报几百上千条 cURL error 28:Connection time-out)。

这么点小功能,代码写的这么臃肿,搞的个大项目似的,都是Java半路出家的吧,接口又时好时不好,调试都不方便调试!

exception 'OSS\Core\OssException' with message 'RequestCoreException: cURL resource: Resource id #12; cURL error: Empty reply from server (52)' in /data/www/xxxx/vendor/aliyuncs/oss-sdk-php/src/OSS/OssClient.php:2187 Stack trace: #0 /data/www/xxxx/vendor/aliyuncs/oss-sdk-php/src/OSS/OssClient.php(1180): OSS\OssClient->auth(Array) #1 /data/www/xxxx/application/libraries/WechatApi.php(142): OSS\OssClient->uploadFile('jy-activity', 'jy-activity/qrc...', '/data/www/...') #2 /data/www/xxxx/application/libraries/service/QrcodeService.php(95): WechatApi->save_mp_qrcode('gQGX7zwAAAAAAAA...', '1530537439', '6') #3 /data/www/xxxx/application/libraries/api/WechatAddQrcode.php(79): Project\service\QrcodeService->createMpQrcode('6', 'QR_SCENE', '1530537439', 111683, '60') #4 /data/www/xxxx/application/controllers/api/wechat.php(114): WechatAddQrcode->execute() #5 [internal function]: Wechat_controller->route() #6 /data/www/xxxx/system/core/CodeIgniter.php(525): call_user_func_array(Array, Array) #7 /data/www/xxxx/index.php(293): require_once('/data/www/...') #8 {main}

windows下 上传本地文件时 路径问题

本地开发windows中 'DIRECTORY_SEPARATOR' IDE解析为 “\”
当上传代码到Linux服务器上时解析 为 "/"
导致本地上传的文件在Linux服务器上无法找到
其实这也不不是一个bug 只是不同的开发环境下 会出现问题而已

请求增加「图片服务」相关的接口

Dear @baiyubin

我是一个 WordPress OSS 插件的维护者,(https://github.com/IvanChou/aliyun-oss-support),在 WordPress 中使用 OSS 大多是对「图片服务」的需求

目前的 SDK 没有应对「图片服务」的相关接口,比如 定义/获取图片样式、设置分割符、获取时候开启了原图保护 等等,对大量 WordPress 和 OSS 的小白用户来说,要配置好这些并不容易,就算是对着教程做遇到的问题仍旧不少

最理想的效果:是在用户无感知的情况下替他们配置好一切

就目前来看,需要几个相关接口的支撑

  1. 配置/修改图片样式规则
  2. 检测 bucket 是否开启了图片保护
  3. 获取图片服务绑定的域名

不知官方目前或将来是否有开放这部分接口的计划,特来询问~

API调用,如何获取ResponseCore或者Result对象

如果调用OssClient::listObjects()出现异常则直接抛OssException,除此之外无法得知HTTP status code和异常信息:比如,InvalidAccessKeyId, SignatureDoesNotMatch.

而之前的老版本是可以的,因为它的接口调用直接返回ResponseCore对象.

求答

尊敬的阿里云用户:

您好,出于安全考虑,从5月20日起,从互联网链路访问OSS国内各区域的网页类型文件的Response Headers中会自动加入Content-Disposition:'attachment=filename; '。从浏览器访问网页类型文件,会以附件形式进行下载。

用户使用自有域名访问OSS的请求,不会加入此Header,使用自有域名访问请参考“绑定自定义域名”,点此查看。

阿里云计算有限公司

2017年4月18日

确认一个非技术问题 你们这个只会影响html类型文件吧,原来的图片没有使用自定义域名应该不会受影响吧?

response-content-disposition不生效

signUrl设置options的response-content-disposition为attachment; filename:testing.jpg不起作用。

正常情况下,该object下载会被重命名为testing.jpg,但是目前下载的还是object原来的名字。

readme文件中中代码错误

$endpoint = "<Domain that you select to access an OSS data center, such as "oss-cn-hangzhou.aliyuncs.com>";

这个引号不对,麻烦改下吧。

设置header头不起作用

使用cliecnt->uploadFile()设置:options 设置:

$options = [
            'Expires' => new DateTime("+5 years"), // 获取5年
            'Cache-Control' => 'public, max-age=31536000' // 缓存一年
        ];

根本就不起作用,而我使用一个非官方的sdk竟然可以

希望提供 Guzzle 版本 :)

提供 Guzzle 版本有很多好处:

  1. 使用 Modern PHP HTTP Client
  2. 方便实现 StreamWrapper
  3. AWS S3 的各种集成框架可以很方便的迁移到 OSS 上
  4. 一言难尽 ...

demo 的 listallobjects

哪个sb写的demo,劳资用了listallobjects后就这样给我加了一百个虚拟目录和文件

检查上传文件的内容是否合法导致不能上传长度为0的文件

@ aliyuncs/oss-sdk-php/src/OSS/Core/OssUtil.php

    /**
     * 检查上传文件的内容是否合法
     *
     * @param $content string
     * @throws OssException
     */
    public static function validateContent($content)
    {
        if (empty($content)) {
            throw new OssException("http body content is invalid");
        }
    }

这就导致如果有明确需求需要上传一个空文件会导致报错,希望能去掉这个判断。
谢谢。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.