| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 | <?phpnamespace Qiniu\Storage;use Qiniu\Config;use Qiniu\Http\Client;use Qiniu\Http\Error;/** * 断点续上传类, 该类主要实现了断点续上传中的分块上传, * 以及相应地创建块和创建文件过程. * * @link http://developer.qiniu.com/docs/v6/api/reference/up/mkblk.html * @link http://developer.qiniu.com/docs/v6/api/reference/up/mkfile.html */final class ResumeUploader{    private $upToken;    private $key;    private $inputStream;    private $size;    private $params;    private $mime;    private $contexts;    private $host;    private $currentUrl;    private $config;    /**     * 上传二进制流到七牛     *     * @param string   $upToken     上传凭证     * @param string   $key         上传文件名     * @param resource $inputStream 上传二进制流     * @param int      $size        上传流的大小     * @param array    $params      自定义变量     * @param string   $mime        上传数据的mimeType     *     * @link http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar     */    public function __construct(        $upToken,        $key,        $inputStream,        $size,        $params = null,        $mime = '',        $config = null    ) {        $this->upToken = $upToken;        $this->key = $key;        $this->inputStream = $inputStream;        $this->size = $size;        $this->params = $params;        $this->mime = $mime ? $mime : 'application/octet-stream';        $this->contexts = array();        $this->config = $config ? $config : new Config();        list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($upToken);        if ($err != null) {            return array(null, $err);        }        $upHost = $this->config->getUpHost($accessKey, $bucket);        if ($err != null) {            throw new \Exception($err->message(), 1);        }        $this->host = $upHost;    }    /**     * 上传操作     */    public function upload($fname)    {        $uploaded = 0;        while ($uploaded < $this->size) {            $blockSize = $this->blockSize($uploaded);            $data = fread($this->inputStream, $blockSize);            if ($data === false) {                throw new \Exception("file read failed", 1);            }            $crc = \Qiniu\crc32_data($data);            $response = $this->makeBlock($data, $blockSize);            $ret = null;            if ($response->ok() && $response->json() != null) {                $ret = $response->json();            }            if ($response->statusCode < 0) {                list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($this->upToken);                if ($err != null) {                    return array(null, $err);                }                $upHostBackup = $this->config->getUpBackupHost($accessKey, $bucket);                $this->host = $upHostBackup;            }            if ($response->needRetry() || !isset($ret['crc32']) || $crc != $ret['crc32']) {                $response = $this->makeBlock($data, $blockSize);                $ret = $response->json();            }            if (!$response->ok() || !isset($ret['crc32']) || $crc != $ret['crc32']) {                return array(null, new Error($this->currentUrl, $response));            }            array_push($this->contexts, $ret['ctx']);            $uploaded += $blockSize;        }        return $this->makeFile($fname);    }    public function uploadChunk($index, $file, $size)    {        $blockSize = $this->size;        $data = fread($this->inputStream, $size);        if ($data === false) {            throw new \Exception("file read failed", 1);        }        $crc = \Qiniu\crc32_data($data);        $response = $this->makeBlock($data, $blockSize);        $ret = null;        if ($response->ok() && $response->json() != null) {            $ret = $response->json();        }        if ($response->statusCode < 0) {            list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($this->upToken);            if ($err != null) {                return array(null, $err);            }            $upHostBackup = $this->config->getUpBackupHost($accessKey, $bucket);            $this->host = $upHostBackup;        }        if ($response->needRetry() || !isset($ret['crc32']) || $crc != $ret['crc32']) {            $response = $this->makeBlock($data, $blockSize);            $ret = $response->json();        }        if (!$response->ok() || !isset($ret['crc32']) || $crc != $ret['crc32']) {            return array(null, new Error($this->currentUrl, $response));        }        array_push($this->contexts, $ret['ctx']);        return $ret;    }    public function setContexts($contexts)    {        $this->contexts = is_array($contexts) ? $contexts : explode(',', $contexts);        return $this;    }    /**     * 创建块     */    private function makeBlock($block, $blockSize)    {        $url = $this->host . '/mkblk/' . $blockSize;        return $this->post($url, $block);    }    private function fileUrl($fname)    {        $url = $this->host . '/mkfile/' . $this->size;        $url .= '/mimeType/' . \Qiniu\base64_urlSafeEncode($this->mime);        if ($this->key != null) {            $url .= '/key/' . \Qiniu\base64_urlSafeEncode($this->key);        }        $url .= '/fname/' . \Qiniu\base64_urlSafeEncode($fname);        if (!empty($this->params)) {            foreach ($this->params as $key => $value) {                $val = \Qiniu\base64_urlSafeEncode($value);                $url .= "/$key/$val";            }        }        return $url;    }    /**     * 创建文件     */    public function makeFile($fname)    {        $url = $this->fileUrl($fname);        $body = implode(',', $this->contexts);        $response = $this->post($url, $body);        if ($response->needRetry()) {            $response = $this->post($url, $body);        }        if (!$response->ok()) {            return array(null, new Error($this->currentUrl, $response));        }        return array($response->json(), null);    }    private function post($url, $data)    {        $this->currentUrl = $url;        $headers = array('Authorization' => 'UpToken ' . $this->upToken);        return Client::post($url, $data, $headers);    }    private function blockSize($uploaded)    {        if ($this->size < $uploaded + Config::BLOCK_SIZE) {            return $this->size - $uploaded;        }        return Config::BLOCK_SIZE;    }}
 |