예제 #1
0
 /**
  * Uploads a part of a multipart object upload
  *
  * @param   Input   $input           Input data. You MUST specify the UploadID and PartNumber
  * @param   string  $bucket          Bucket name
  * @param   string  $uri             Object URI
  * @param   array   $requestHeaders  Array of request headers or content type as a string
  * @param   int     $chunkSize       Size of each upload chunk, in bytes. It cannot be less than 5242880 bytes (5Mb)
  *
  * @return  null|string  The ETag of the upload part of null if we have ran out of parts to upload
  */
 public function uploadMultipart(Input $input, $bucket, $uri, $requestHeaders = array(), $chunkSize = 5242880)
 {
     if ($chunkSize < 5242880) {
         $chunkSize = 5242880;
     }
     // We need a valid UploadID and PartNumber
     $UploadID = $input->getUploadID();
     $PartNumber = $input->getPartNumber();
     if (empty($UploadID)) {
         throw new CannotPutFile(__METHOD__ . '(): No UploadID specified');
     }
     if (empty($PartNumber)) {
         throw new CannotPutFile(__METHOD__ . '(): No PartNumber specified');
     }
     $UploadID = urlencode($UploadID);
     $PartNumber = (int) $PartNumber;
     $request = new Request('PUT', $bucket, $uri, $this->configuration);
     $request->setParameter('partNumber', $PartNumber);
     $request->setParameter('uploadId', $UploadID);
     $request->setInput($input);
     // Full data length
     $totalSize = $input->getSize();
     // No Content-Type for multipart uploads
     $input->setType(null);
     // Calculate part offset
     $partOffset = $chunkSize * ($PartNumber - 1);
     if ($partOffset > $totalSize) {
         // This is to signify that we ran out of parts ;)
         return null;
     }
     // How many parts are there?
     $totalParts = floor($totalSize / $chunkSize);
     if ($totalParts * $chunkSize < $totalSize) {
         $totalParts++;
     }
     // Calculate Content-Length
     $size = $chunkSize;
     if ($PartNumber >= $totalParts) {
         $size = $totalSize - ($PartNumber - 1) * $chunkSize;
     }
     if ($size <= 0) {
         // This is to signify that we ran out of parts ;)
         return null;
     }
     $input->setSize($size);
     switch ($input->getInputType()) {
         case Input::INPUT_DATA:
             $input->setData(substr($input->getData(), ($PartNumber - 1) * $chunkSize, $input->getSize()));
             break;
         case Input::INPUT_FILE:
         case Input::INPUT_RESOURCE:
             $fp = $input->getFp();
             fseek($fp, ($PartNumber - 1) * $chunkSize);
             break;
     }
     // Custom request headers (Content-Type, Content-Disposition, Content-Encoding)
     if (is_array($requestHeaders)) {
         foreach ($requestHeaders as $h => $v) {
             if (strtolower(substr($h, 0, 6)) == 'x-amz-') {
                 $request->setAmzHeader(strtolower($h), $v);
             } else {
                 $request->setHeader($h, $v);
             }
         }
     }
     $request->setHeader('Content-Length', $input->getSize());
     $response = $request->getResponse();
     if ($response->code !== 200) {
         if (!$response->error->isError()) {
             $response->error = new Error($response->code, "Unexpected HTTP status {$response->code}");
         }
         if (is_object($response->body) && $response->body instanceof \SimpleXMLElement && strpos($input->getSize(), ',') === false) {
             // For some moronic reason, trying to multipart upload files on some hosts comes back with a stupid
             // error from Amazon that we need to set Content-Length:5242880,5242880 instead of
             // Content-Length:5242880 which is AGAINST Amazon's documentation. In this case we pass the header
             // 'workaround-braindead-error-from-amazon' and retry. Screw you too, Amazon, you bloody idiots!
             if (isset($response->body->CanonicalRequest)) {
                 $amazonsCanonicalRequest = (string) $response->body->CanonicalRequest;
                 $lines = explode("\n", $amazonsCanonicalRequest);
                 foreach ($lines as $line) {
                     if (substr($line, 0, 15) != 'content-length:') {
                         continue;
                     }
                     list($junk, $stupidAmazonDefinedContentLength) = explode(":", $line);
                     if (strpos($stupidAmazonDefinedContentLength, ',') !== false) {
                         if (!isset($requestHeaders['workaround-braindead-error-from-amazon'])) {
                             $requestHeaders['workaround-braindead-error-from-amazon'] = 'you can\'t fix stupid';
                             return $this->uploadMultipart($input, $bucket, $uri, $requestHeaders, $chunkSize);
                         }
                     }
                 }
             }
         }
         throw new CannotPutFile(sprintf(__METHOD__ . "(): [%s] %s\n\nDebug info:\n%s", $response->error->getCode(), $response->error->getMessage(), print_r($response->body, true)));
     }
     // Return the ETag header
     return $response->headers['hash'];
 }