diff --git a/qiniu/io.php b/qiniu/io.php index 8c5a7488..10df1b24 100644 --- a/qiniu/io.php +++ b/qiniu/io.php @@ -14,7 +14,7 @@ class Qiniu_PutExtra public $CheckCrc = 0; } -function Qiniu_Put($upToken, $key, $body, $putExtra) // => ($data, $err) +function Qiniu_Put($upToken, $key, $body, $putExtra) // => ($putRet, $err) { global $QINIU_UP_HOST; @@ -39,7 +39,7 @@ function Qiniu_Put($upToken, $key, $body, $putExtra) // => ($data, $err) return Qiniu_Client_CallWithMultipartForm($client, $QINIU_UP_HOST, $fields, $files); } -function Qiniu_PutFile($upToken, $key, $localFile, $putExtra) // => ($data, $err) +function Qiniu_PutFile($upToken, $key, $localFile, $putExtra) // => ($putRet, $err) { global $QINIU_UP_HOST; @@ -67,3 +67,5 @@ function Qiniu_PutFile($upToken, $key, $localFile, $putExtra) // => ($data, $err return Qiniu_Client_CallWithForm($client, $QINIU_UP_HOST, $fields, 'multipart/form-data'); } +// ---------------------------------------------------------- + diff --git a/qiniu/resumable_io.php b/qiniu/resumable_io.php new file mode 100644 index 00000000..7e477a73 --- /dev/null +++ b/qiniu/resumable_io.php @@ -0,0 +1,143 @@ +Bucket = $bucket; + } +} + +// ---------------------------------------------------------- +// func Qiniu_Rio_BlockCount + +define('QINIU_RIO_BLOCK_BITS', 22); +define('QINIU_RIO_BLOCK_SIZE', 1 << QINIU_RIO_BLOCK_BITS); // 4M + +function Qiniu_Rio_BlockCount($fsize) // => $blockCnt +{ + return ($fsize + (QINIU_RIO_BLOCK_SIZE - 1)) >> QINIU_RIO_BLOCK_BITS; +} + +// ---------------------------------------------------------- +// internal func Qiniu_Rio_Mkblock/Mkfile + +function Qiniu_Rio_Mkblock($self, $host, $reader, $size) // => ($blkputRet, $err) +{ + if (is_resource($reader)) { + $body = fread($reader, $size); + if ($body === false) { + $err = Qiniu_NewError(0, 'fread failed'); + return array(null, $err); + } + } else { + list($body, $err) = $reader->Read($size); + if ($err !== null) { + return array(null, $err); + } + } + if (strlen($body) != $size) { + $err = Qiniu_NewError(0, 'fread failed: unexpected eof'); + return array(null, $err); + } + + $url = $host . '/mkblk/' . $size; + return Qiniu_Client_CallWithForm($self, $url, $body, 'application/octet-stream'); +} + +function Qiniu_Rio_Mkfile($self, $host, $key, $fsize, $extra) // => ($putRet, $err) +{ + $entry = $extra->Bucket . ':' . $key; + $url = $host . '/rs-mkfile/' . Qiniu_Encode($entry) . '/fsize/' . $fsize; + + if (!empty($extra->MimeType)) { + $url .= '/mimeType/' . Qiniu_Encode($extra->MimeType); + } + + $ctxs = array(); + foreach ($extra->Progresses as $prog) { + $ctxs []= $prog['ctx']; + } + $body = implode(',', $ctxs); + + return Qiniu_Client_CallWithForm($self, $url, $body, 'text/plain'); +} + +// ---------------------------------------------------------- +// class Qiniu_Rio_UploadClient + +class Qiniu_Rio_UploadClient +{ + public $uptoken; + + public function __construct($uptoken) + { + $this->uptoken = $uptoken; + } + + public function RoundTrip($req) // => ($resp, $error) + { + $token = $this->uptoken; + $req->Header['Authorization'] = "UpToken $token"; + return Qiniu_Client_do($req); + } +} + +// ---------------------------------------------------------- +// class Qiniu_Rio_Put/PutFile + +function Qiniu_Rio_Put($upToken, $key, $body, $fsize, $putExtra) // => ($putRet, $err) +{ + global $QINIU_UP_HOST; + + $self = new Qiniu_Rio_UploadClient($upToken); + + $progresses = array(); + $host = $QINIU_UP_HOST; + $uploaded = 0; + while ($uploaded < $fsize) { + if ($fsize < $uploaded + QINIU_RIO_BLOCK_SIZE) { + $bsize = $fsize - $uploaded; + } else { + $bsize = QINIU_RIO_BLOCK_SIZE; + } + list($blkputRet, $err) = Qiniu_Rio_Mkblock($self, $host, $body, $bsize); + $host = $blkputRet['host']; + $uploaded += $bsize; + $progresses []= $blkputRet; + } + + $putExtra->Progresses = $progresses; + return Qiniu_Rio_Mkfile($self, $host, $key, $fsize, $putExtra); +} + +function Qiniu_Rio_PutFile($upToken, $key, $localFile, $putExtra) // => ($putRet, $err) +{ + $fp = fopen($localFile, 'rb'); + if ($fp === false) { + $err = Qiniu_NewError(0, 'fopen failed'); + return array(null, $err); + } + + $fi = fstat($fp); + $result = Qiniu_Rio_Put($upToken, $key, $fp, $fi['size'], $putExtra); + fclose($fp); + return $result; +} + +// ---------------------------------------------------------- + diff --git a/qiniu/rs.php b/qiniu/rs.php index c1471412..579c533e 100644 --- a/qiniu/rs.php +++ b/qiniu/rs.php @@ -170,9 +170,8 @@ function Qiniu_RS_Copy($self, $bucketSrc, $keySrc, $bucketDest, $keyDest) // => return Qiniu_Client_CallNoRet($self, $QINIU_RS_HOST . $uri); } - // ---------------------------------------------------------- -//batch +// batch function Qiniu_RS_Batch($self, $ops) // => ($data, $error) { @@ -222,3 +221,5 @@ function Qiniu_RS_BatchCopy($self, $entryPairs) return Qiniu_RS_Batch($self, $params); } +// ---------------------------------------------------------- + diff --git a/qiniu/rs_utils.php b/qiniu/rs_utils.php index 79421bae..15343d62 100644 --- a/qiniu/rs_utils.php +++ b/qiniu/rs_utils.php @@ -2,18 +2,43 @@ require_once("rs.php"); require_once("io.php"); +require_once("resumable_io.php"); -function Qiniu_RS_Put($self, $bucket, $key, $body, $putExtra) // => ($data, $err) +function Qiniu_RS_Put($self, $bucket, $key, $body, $putExtra) // => ($putRet, $err) { $putPolicy = new Qiniu_RS_PutPolicy("$bucket:$key"); $upToken = $putPolicy->Token($self->Mac); return Qiniu_Put($upToken, $key, $body, $putExtra); } -function Qiniu_RS_PutFile($self, $bucket, $key, $localFile, $putExtra) // => ($data, $err) +function Qiniu_RS_PutFile($self, $bucket, $key, $localFile, $putExtra) // => ($putRet, $err) { $putPolicy = new Qiniu_RS_PutPolicy("$bucket:$key"); $upToken = $putPolicy->Token($self->Mac); return Qiniu_PutFile($upToken, $key, $localFile, $putExtra); } +function Qiniu_RS_Rput($self, $bucket, $key, $body, $fsize, $putExtra) // => ($putRet, $err) +{ + $putPolicy = new Qiniu_RS_PutPolicy("$bucket:$key"); + $upToken = $putPolicy->Token($self->Mac); + if ($putExtra == null) { + $putExtra = new Qiniu_Rio_PutExtra($bucket); + } else { + $putExtra->Bucket = $bucket; + } + return Qiniu_Rio_Put($upToken, $key, $body, $fsize, $putExtra); +} + +function Qiniu_RS_RputFile($self, $bucket, $key, $localFile, $putExtra) // => ($putRet, $err) +{ + $putPolicy = new Qiniu_RS_PutPolicy("$bucket:$key"); + $upToken = $putPolicy->Token($self->Mac); + if ($putExtra == null) { + $putExtra = new Qiniu_Rio_PutExtra($bucket); + } else { + $putExtra->Bucket = $bucket; + } + return Qiniu_Rio_PutFile($upToken, $key, $localFile, $putExtra); +} + diff --git a/tests/RioTest.php b/tests/RioTest.php new file mode 100644 index 00000000..1e195b04 --- /dev/null +++ b/tests/RioTest.php @@ -0,0 +1,71 @@ +client = new Qiniu_MacHttpClient(null); + $this->bucket = getenv("QINIU_BUCKET_NAME"); + } + + public function testMockReader() + { + $reader = new MockReader; + list($data) = $reader->Read(5); + $this->assertEquals($data, "ABCDE"); + + list($data) = $reader->Read(27); + $this->assertEquals($data, "FGHIJKLMNOPQRSTUVWXYZABCDEF"); + } + + public function testPut() + { + $key = 'testRioPut' . getTid(); + $err = Qiniu_RS_Delete($this->client, $this->bucket, $key); + + $putPolicy = new Qiniu_RS_PutPolicy($this->bucket); + $upToken = $putPolicy->Token(null); + $putExtra = new Qiniu_Rio_PutExtra($this->bucket); + $reader = new MockReader; + list($ret, $err) = Qiniu_Rio_Put($upToken, $key, $reader, 5, $putExtra); + $this->assertNull($err); + $this->assertEquals($ret['hash'], "Fnvgeq9GDVk6Mj0Nsz2gW2S_3LOl"); + var_dump($ret); + + list($ret, $err) = Qiniu_RS_Stat($this->client, $this->bucket, $key); + $this->assertNull($err); + var_dump($ret); + + $err = Qiniu_RS_Delete($this->client, $this->bucket, $key); + $this->assertNull($err); + } + + public function testLargePut() + { + $key = 'testRioLargePut' . getTid(); + $err = Qiniu_RS_Delete($this->client, $this->bucket, $key); + + $putPolicy = new Qiniu_RS_PutPolicy($this->bucket); + $upToken = $putPolicy->Token(null); + $putExtra = new Qiniu_Rio_PutExtra($this->bucket); + $reader = new MockReader; + list($ret, $err) = Qiniu_Rio_Put($upToken, $key, $reader, QINIU_RIO_BLOCK_SIZE + 5, $putExtra); + $this->assertNull($err); + $this->assertEquals($ret['hash'], "lgQEOCZ8Ievliq8XOfZmWTndgOll"); + var_dump($ret); + + list($ret, $err) = Qiniu_RS_Stat($this->client, $this->bucket, $key); + $this->assertNull($err); + var_dump($ret); + + $err = Qiniu_RS_Delete($this->client, $this->bucket, $key); + $this->assertNull($err); + } +} + diff --git a/tests/RsUtilsTest.php b/tests/RsUtilsTest.php index dded1764..6b8123a5 100644 --- a/tests/RsUtilsTest.php +++ b/tests/RsUtilsTest.php @@ -14,6 +14,43 @@ public function setUp() $this->bucket = getenv("QINIU_BUCKET_NAME"); } + public function testRput() + { + $key = 'tmp/testRput' . getTid(); + $err = Qiniu_RS_Delete($this->client, $this->bucket, $key); + + $reader = new MockReader; + list($ret, $err) = Qiniu_RS_Rput($this->client, $this->bucket, $key, $reader, 5, null); + $this->assertNull($err); + var_dump($ret); + $this->assertEquals($ret['hash'], "Fnvgeq9GDVk6Mj0Nsz2gW2S_3LOl"); + + list($ret, $err) = Qiniu_RS_Stat($this->client, $this->bucket, $key); + $this->assertNull($err); + var_dump($ret); + + $err = Qiniu_RS_Delete($this->client, $this->bucket, $key); + $this->assertNull($err); + } + + public function testRputFile() + { + $key = 'tmp/testRputFile' . getTid(); + $err = Qiniu_RS_Delete($this->client, $this->bucket, $key); + + list($ret, $err) = Qiniu_RS_RputFile($this->client, $this->bucket, $key, __file__, null); + $this->assertNull($err); + var_dump($ret); + $this->assertArrayHasKey('hash', $ret); + + list($ret, $err) = Qiniu_RS_Stat($this->client, $this->bucket, $key); + $this->assertNull($err); + var_dump($ret); + + $err = Qiniu_RS_Delete($this->client, $this->bucket, $key); + $this->assertNull($err); + } + public function testPutFile() { $key = 'tmp/testPutFile' . getTid(); @@ -39,7 +76,7 @@ public function testPut() $key = 'tmp/testPut' . getTid(); $err = Qiniu_RS_Delete($this->client, $this->bucket, $key); - list($ret, $err) = Qiniu_RS_Put($this->client, $this->bucket, $key, "hello world!", null); + list($ret, $err) = Qiniu_RS_Put($this->client, $this->bucket, $key, 'hello world!', null); $this->assertNull($err); $this->assertArrayHasKey('hash', $ret); var_dump($ret); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index ddfe8a56..3757bfac 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -24,3 +24,25 @@ function getTid() { return $tid; } +class MockReader +{ + private $off = 0; + + public function __construct($off = 0) + { + $this->off = $off; + } + + public function Read($bytes) // => ($data, $err) + { + $off = $this->off; + $data = ''; + for ($i = 0; $i < $bytes; $i++) { + $data .= chr(65 + ($off % 26)); // ord('A') = 65 + $off++; + } + $this->off = $off; + return array($data, null); + } +} +