/** * Copy an object * * @param string $bucket Source bucket name * @param string $uri Source object URI * @param string $bucket Destination bucket name * @param string $uri Destination object URI * @param constant $acl ACL constant * @param array $metaHeaders Optional array of x-amz-meta-* headers * @param array $requestHeaders Optional array of request headers (content type, disposition, etc.) * @param constant $storageClass Storage class constant * @return mixed | false */ public static function copyObject($srcBucket, $srcUri, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD) { $rest = new AEUtilsS3Request('PUT', $bucket, $uri, AEUtilAmazons3::getInstance()->defaultHost); $rest->setHeader('Content-Length', 0); foreach ($requestHeaders as $h => $v) { $rest->setHeader($h, $v); } foreach ($metaHeaders as $h => $v) { $rest->setAmzHeader('x-amz-meta-' . $h, $v); } if ($storageClass !== self::STORAGE_CLASS_STANDARD) { // Storage class $rest->setAmzHeader('x-amz-storage-class', $storageClass); } $rest->setAmzHeader('x-amz-acl', $acl); $rest->setAmzHeader('x-amz-copy-source', sprintf('/%s/%s', $srcBucket, rawurlencode($srcUri))); if (sizeof($requestHeaders) > 0 || sizeof($metaHeaders) > 0) { $rest->setAmzHeader('x-amz-metadata-directive', 'REPLACE'); } $rest = $rest->getResponse(); if ($rest->error === false && $rest->code !== 200) { $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); } if ($rest->error !== false) { $o = self::getInstance(); $o->setWarning(sprintf(__CLASS__ . "::copyObject({$srcBucket}, {$srcUri}, {$bucket}, {$uri}): [%s] %s", $rest->error['code'], $rest->error['message'])); return false; } return true; }
/** * Uploads a part of a multipart object upload * * @param mixed $input Input data. You MUST specify the UploadID and PartNumber * @param string $bucket Bucket name * @param string $uri Object URI * @param constant $acl ACL constant * @param array $metaHeaders Array of x-amz-meta-* headers (NOT USED) * @param array $requestHeaders Array of request headers or content type as a string * @return boolean */ public static function uploadMultipart($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array()) { if ($input === false) { $o = self::getInstance(); $o->setWarning(__CLASS__ . "::uploadMultipart(): No input specified"); return false; } if (is_string($input)) { $input = array('data' => $input, 'size' => strlen($input), 'md5sum' => base64_encode(md5($input, true))); } // We need a valid UploadID and PartNumber if (!array_key_exists('UploadID', $input)) { $o = self::getInstance(); $o->setWarning(__CLASS__ . "::uploadMultipart(): No UploadID specified"); return false; } if (!array_key_exists('PartNumber', $input)) { $o = self::getInstance(); $o->setWarning(__CLASS__ . "::uploadMultipart(): No PartNumber specified"); return false; } $UploadID = $input['UploadID']; $UploadID = urlencode($UploadID); $PartNumber = (int) $input['PartNumber']; // DEBUG //AEUtilLogger::WriteLog(_AE_LOG_DEBUG, '*** Upload ID: '.$UploadID); //AEUtilLogger::WriteLog(_AE_LOG_DEBUG, '*** Part Number: '.$PartNumber); $rest = new AEUtilsS3Request('PUT', $bucket, $uri, 's3.amazonaws.com', AEUtilAmazons3::getInstance()->defaultHost); $rest->setParameter('partNumber', $PartNumber); $rest->setParameter('uploadId', $UploadID); // Data if (isset($input['fp'])) { $rest->fp = $input['fp']; } elseif (isset($input['file'])) { $rest->fp = @fopen($input['file'], 'rb'); } elseif (isset($input['data'])) { $rest->data = $input['data']; } // Full data length if (isset($input['size']) && $input['size'] >= 0) { $totalSize = $input['size']; } else { if (isset($input['file'])) { $totalSize = filesize($input['file']); } elseif (isset($input['data'])) { $totalSize = strlen($input['data']); } } // No Content-Type for multipart uploads if (array_key_exists('type', $input)) { unset($input['type']); } // Calculate part offset $partOffset = 5242880 * ($PartNumber - 1); if ($partOffset > $totalSize) { return 0; // This is to signify that we ran out of parts ;) } // How many parts are there? $totalParts = floor($totalSize / 5242880); if ($totalParts * 5242880 < $totalSize) { $totalParts++; } // Calculate Content-Length if ($PartNumber == $totalParts) { $rest->size = $totalSize - ($PartNumber - 1) * 5242880; } else { $rest->size = 5242880; } if (isset($input['file'])) { // Create a temp file with the bytes we want to upload $fp = @fopen($input['file'], 'rb'); $result = fseek($fp, ($PartNumber - 1) * 5242880); $rest->fp = $fp; // I have to set the AEUtilS3Request's file pointer, NOT the file structure's, as the request object is already initialized $rest->data = false; } elseif (isset($input['data'])) { $tempfilename = null; $rest->fp = false; $rest->data = substr($input['data'], ($PartNumber - 1) * 5242880, $rest->size); } // 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-') { $rest->setAmzHeader($h, $v); } else { $rest->setHeader($h, $v); } } } elseif (is_string($requestHeaders)) { // Support for legacy contentType parameter $input['type'] = $requestHeaders; } // We need to post with Content-Length if ($rest->size >= 0 && ($rest->fp !== false || $rest->data !== false)) { $rest->getResponse(); } else { if ($rest->size < 0) { $rest->response->error = array('code' => 0, 'message' => 'Missing file size parameter'); } else { $rest->response->error = array('code' => 0, 'message' => 'No data file pointer specified'); } } @fclose($fp); if ($rest->response->error === false && $rest->response->code !== 200) { $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); } if ($rest->response->error !== false) { // Sometimes we get a broken pipe error which is bogus; it's just a response with 0 size if ($rest->response->error['code'] == 55 && $rest->response->code == 200 && is_array($rest->response->headers)) { // This is not an error. AAAAARGH! return $rest->response->headers['hash']; } else { $o = self::getInstance(); $o->setWarning(sprintf(__CLASS__ . "::uploadMultipart(): [%s] %s", $rest->response->error['code'], $rest->response->error['message'])); return false; } } // Return the ETag header return $rest->response->headers['hash']; }