diff -r 5b37998e522e -r 162c1de6545a web/lib/Zend/Service/WindowsAzure/Storage/Blob.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/lib/Zend/Service/WindowsAzure/Storage/Blob.php Fri Mar 11 15:05:35 2011 +0100 @@ -0,0 +1,2001 @@ +_apiVersion = '2009-09-19'; + + // SharedAccessSignature credentials + $this->_sharedAccessSignatureCredentials = new Zend_Service_WindowsAzure_Credentials_SharedAccessSignature($accountName, $accountKey, $usePathStyleUri); + } + + /** + * Check if a blob exists + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param string $snapshotId Snapshot identifier + * @return boolean + */ + public function blobExists($containerName = '', $blobName = '', $snapshotId = null) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + + // Get blob instance + try { + $this->getBlobInstance($containerName, $blobName, $snapshotId); + } catch (Zend_Service_WindowsAzure_Exception $e) { + return false; + } + + return true; + } + + /** + * Check if a container exists + * + * @param string $containerName Container name + * @return boolean + */ + public function containerExists($containerName = '') + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + + // List containers + $containers = $this->listContainers($containerName, 1); + foreach ($containers as $container) { + if ($container->Name == $containerName) { + return true; + } + } + + return false; + } + + /** + * Create container + * + * @param string $containerName Container name + * @param array $metadata Key/value pairs of meta data + * @return object Container properties + * @throws Zend_Service_WindowsAzure_Exception + */ + public function createContainer($containerName = '', $metadata = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if (!is_array($metadata)) { + throw new Zend_Service_WindowsAzure_Exception('Meta data should be an array of key and value pairs.'); + } + + // Create metadata headers + $headers = array(); + $headers = array_merge($headers, $this->_generateMetadataHeaders($metadata)); + + // Perform request + $response = $this->_performRequest($containerName, '?restype=container', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if ($response->isSuccessful()) { + return new Zend_Service_WindowsAzure_Storage_BlobContainer( + $containerName, + $response->getHeader('Etag'), + $response->getHeader('Last-modified'), + $metadata + ); + } else { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Get container ACL + * + * @param string $containerName Container name + * @param bool $signedIdentifiers Display only private/blob/container or display signed identifiers? + * @return string Acl, to be compared with Zend_Service_WindowsAzure_Storage_Blob::ACL_* + * @throws Zend_Service_WindowsAzure_Exception + */ + public function getContainerAcl($containerName = '', $signedIdentifiers = false) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + + // Perform request + $response = $this->_performRequest($containerName, '?restype=container&comp=acl', Zend_Http_Client::GET, array(), false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ); + if ($response->isSuccessful()) { + if ($signedIdentifiers == false) { + // Only private/blob/container + $accessType = $response->getHeader(Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'blob-public-access'); + if (strtolower($accessType) == 'true') { + $accessType = self::ACL_PUBLIC_CONTAINER; + } + return $accessType; + } else { + // Parse result + $result = $this->_parseResponse($response); + if (!$result) { + return array(); + } + + $entries = null; + if ($result->SignedIdentifier) { + if (count($result->SignedIdentifier) > 1) { + $entries = $result->SignedIdentifier; + } else { + $entries = array($result->SignedIdentifier); + } + } + + // Return value + $returnValue = array(); + foreach ($entries as $entry) { + $returnValue[] = new Zend_Service_WindowsAzure_Storage_SignedIdentifier( + $entry->Id, + $entry->AccessPolicy ? $entry->AccessPolicy->Start ? $entry->AccessPolicy->Start : '' : '', + $entry->AccessPolicy ? $entry->AccessPolicy->Expiry ? $entry->AccessPolicy->Expiry : '' : '', + $entry->AccessPolicy ? $entry->AccessPolicy->Permission ? $entry->AccessPolicy->Permission : '' : '' + ); + } + + // Return + return $returnValue; + } + } else { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Set container ACL + * + * @param string $containerName Container name + * @param bool $acl Zend_Service_WindowsAzure_Storage_Blob::ACL_* + * @param array $signedIdentifiers Signed identifiers + * @throws Zend_Service_WindowsAzure_Exception + */ + public function setContainerAcl($containerName = '', $acl = self::ACL_PRIVATE, $signedIdentifiers = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + + // Headers + $headers = array(); + + // Acl specified? + if ($acl != self::ACL_PRIVATE && $acl !== null && $acl != '') { + $headers[Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'blob-public-access'] = $acl; + } + + // Policies + $policies = null; + if (is_array($signedIdentifiers) && count($signedIdentifiers) > 0) { + $policies = ''; + $policies .= '' . "\r\n"; + $policies .= '' . "\r\n"; + foreach ($signedIdentifiers as $signedIdentifier) { + $policies .= ' ' . "\r\n"; + $policies .= ' ' . $signedIdentifier->Id . '' . "\r\n"; + $policies .= ' ' . "\r\n"; + if ($signedIdentifier->Start != '') + $policies .= ' ' . $signedIdentifier->Start . '' . "\r\n"; + if ($signedIdentifier->Expiry != '') + $policies .= ' ' . $signedIdentifier->Expiry . '' . "\r\n"; + if ($signedIdentifier->Permissions != '') + $policies .= ' ' . $signedIdentifier->Permissions . '' . "\r\n"; + $policies .= ' ' . "\r\n"; + $policies .= ' ' . "\r\n"; + } + $policies .= '' . "\r\n"; + } + + // Perform request + $response = $this->_performRequest($containerName, '?restype=container&comp=acl', Zend_Http_Client::PUT, $headers, false, $policies, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if (!$response->isSuccessful()) { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Get container + * + * @param string $containerName Container name + * @return Zend_Service_WindowsAzure_Storage_BlobContainer + * @throws Zend_Service_WindowsAzure_Exception + */ + public function getContainer($containerName = '') + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + + // Perform request + $response = $this->_performRequest($containerName, '?restype=container', Zend_Http_Client::GET, array(), false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ); + if ($response->isSuccessful()) { + // Parse metadata + $metadata = $this->_parseMetadataHeaders($response->getHeaders()); + + // Return container + return new Zend_Service_WindowsAzure_Storage_BlobContainer( + $containerName, + $response->getHeader('Etag'), + $response->getHeader('Last-modified'), + $metadata + ); + } else { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Get container metadata + * + * @param string $containerName Container name + * @return array Key/value pairs of meta data + * @throws Zend_Service_WindowsAzure_Exception + */ + public function getContainerMetadata($containerName = '') + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + + return $this->getContainer($containerName)->Metadata; + } + + /** + * Set container metadata + * + * Calling the Set Container Metadata operation overwrites all existing metadata that is associated with the container. It's not possible to modify an individual name/value pair. + * + * @param string $containerName Container name + * @param array $metadata Key/value pairs of meta data + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. + * @throws Zend_Service_WindowsAzure_Exception + */ + public function setContainerMetadata($containerName = '', $metadata = array(), $additionalHeaders = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if (!is_array($metadata)) { + throw new Zend_Service_WindowsAzure_Exception('Meta data should be an array of key and value pairs.'); + } + if (count($metadata) == 0) { + return; + } + + // Create metadata headers + $headers = array(); + $headers = array_merge($headers, $this->_generateMetadataHeaders($metadata)); + + // Additional headers? + foreach ($additionalHeaders as $key => $value) { + $headers[$key] = $value; + } + + // Perform request + $response = $this->_performRequest($containerName, '?restype=container&comp=metadata', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if (!$response->isSuccessful()) { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Delete container + * + * @param string $containerName Container name + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. + * @throws Zend_Service_WindowsAzure_Exception + */ + public function deleteContainer($containerName = '', $additionalHeaders = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + + // Additional headers? + $headers = array(); + foreach ($additionalHeaders as $key => $value) { + $headers[$key] = $value; + } + + // Perform request + $response = $this->_performRequest($containerName, '?restype=container', Zend_Http_Client::DELETE, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if (!$response->isSuccessful()) { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * List containers + * + * @param string $prefix Optional. Filters the results to return only containers whose name begins with the specified prefix. + * @param int $maxResults Optional. Specifies the maximum number of containers to return per call to Azure storage. This does NOT affect list size returned by this function. (maximum: 5000) + * @param string $marker Optional string value that identifies the portion of the list to be returned with the next list operation. + * @param string $include Optional. Include this parameter to specify that the container's metadata be returned as part of the response body. (allowed values: '', 'metadata') + * @param int $currentResultCount Current result count (internal use) + * @return array + * @throws Zend_Service_WindowsAzure_Exception + */ + public function listContainers($prefix = null, $maxResults = null, $marker = null, $include = null, $currentResultCount = 0) + { + // Build query string + $queryString = array('comp=list'); + if ($prefix !== null) { + $queryString[] = 'prefix=' . $prefix; + } + if ($maxResults !== null) { + $queryString[] = 'maxresults=' . $maxResults; + } + if ($marker !== null) { + $queryString[] = 'marker=' . $marker; + } + if ($include !== null) { + $queryString[] = 'include=' . $include; + } + $queryString = self::createQueryStringFromArray($queryString); + + // Perform request + $response = $this->_performRequest('', $queryString, Zend_Http_Client::GET, array(), false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_LIST); + if ($response->isSuccessful()) { + $xmlContainers = $this->_parseResponse($response)->Containers->Container; + $xmlMarker = (string)$this->_parseResponse($response)->NextMarker; + + $containers = array(); + if ($xmlContainers !== null) { + for ($i = 0; $i < count($xmlContainers); $i++) { + $containers[] = new Zend_Service_WindowsAzure_Storage_BlobContainer( + (string)$xmlContainers[$i]->Name, + (string)$xmlContainers[$i]->Etag, + (string)$xmlContainers[$i]->LastModified, + $this->_parseMetadataElement($xmlContainers[$i]) + ); + } + } + $currentResultCount = $currentResultCount + count($containers); + if ($maxResults !== null && $currentResultCount < $maxResults) { + if ($xmlMarker !== null && $xmlMarker != '') { + $containers = array_merge($containers, $this->listContainers($prefix, $maxResults, $xmlMarker, $include, $currentResultCount)); + } + } + if ($maxResults !== null && count($containers) > $maxResults) { + $containers = array_slice($containers, 0, $maxResults); + } + + return $containers; + } else { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Put blob + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param string $localFileName Local file name to be uploaded + * @param array $metadata Key/value pairs of meta data + * @param string $leaseId Lease identifier + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. + * @return object Partial blob properties + * @throws Zend_Service_WindowsAzure_Exception + */ + public function putBlob($containerName = '', $blobName = '', $localFileName = '', $metadata = array(), $leaseId = null, $additionalHeaders = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($localFileName === '') { + throw new Zend_Service_WindowsAzure_Exception('Local file name is not specified.'); + } + if (!file_exists($localFileName)) { + throw new Zend_Service_WindowsAzure_Exception('Local file not found.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + + // Check file size + if (filesize($localFileName) >= self::MAX_BLOB_SIZE) { + return $this->putLargeBlob($containerName, $blobName, $localFileName, $metadata, $leaseId); + } + + // Put the data to Windows Azure Storage + return $this->putBlobData($containerName, $blobName, file_get_contents($localFileName), $metadata, $leaseId, $additionalHeaders); + } + + /** + * Put blob data + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param mixed $data Data to store + * @param array $metadata Key/value pairs of meta data + * @param string $leaseId Lease identifier + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. + * @return object Partial blob properties + * @throws Zend_Service_WindowsAzure_Exception + */ + public function putBlobData($containerName = '', $blobName = '', $data = '', $metadata = array(), $leaseId = null, $additionalHeaders = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + + // Create metadata headers + $headers = array(); + if ($leaseId !== null) { + $headers['x-ms-lease-id'] = $leaseId; + } + $headers = array_merge($headers, $this->_generateMetadataHeaders($metadata)); + + // Additional headers? + foreach ($additionalHeaders as $key => $value) { + $headers[$key] = $value; + } + + // Specify blob type + $headers[Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'blob-type'] = self::BLOBTYPE_BLOCK; + + // Resource name + $resourceName = self::createResourceName($containerName , $blobName); + + // Perform request + $response = $this->_performRequest($resourceName, '', Zend_Http_Client::PUT, $headers, false, $data, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if ($response->isSuccessful()) { + return new Zend_Service_WindowsAzure_Storage_BlobInstance( + $containerName, + $blobName, + null, + $response->getHeader('Etag'), + $response->getHeader('Last-modified'), + $this->getBaseUrl() . '/' . $containerName . '/' . $blobName, + strlen($data), + '', + '', + '', + false, + $metadata + ); + } else { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Put large blob (> 64 MB) + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param string $localFileName Local file name to be uploaded + * @param array $metadata Key/value pairs of meta data + * @param string $leaseId Lease identifier + * @return object Partial blob properties + * @throws Zend_Service_WindowsAzure_Exception + */ + public function putLargeBlob($containerName = '', $blobName = '', $localFileName = '', $metadata = array(), $leaseId = null) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($localFileName === '') { + throw new Zend_Service_WindowsAzure_Exception('Local file name is not specified.'); + } + if (!file_exists($localFileName)) { + throw new Zend_Service_WindowsAzure_Exception('Local file not found.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + + // Check file size + if (filesize($localFileName) < self::MAX_BLOB_SIZE) { + return $this->putBlob($containerName, $blobName, $localFileName, $metadata); + } + + // Determine number of parts + $numberOfParts = ceil( filesize($localFileName) / self::MAX_BLOB_TRANSFER_SIZE ); + + // Generate block id's + $blockIdentifiers = array(); + for ($i = 0; $i < $numberOfParts; $i++) { + $blockIdentifiers[] = $this->_generateBlockId($i); + } + + // Open file + $fp = fopen($localFileName, 'r'); + if ($fp === false) { + throw new Zend_Service_WindowsAzure_Exception('Could not open local file.'); + } + + // Upload parts + for ($i = 0; $i < $numberOfParts; $i++) { + // Seek position in file + fseek($fp, $i * self::MAX_BLOB_TRANSFER_SIZE); + + // Read contents + $fileContents = fread($fp, self::MAX_BLOB_TRANSFER_SIZE); + + // Put block + $this->putBlock($containerName, $blobName, $blockIdentifiers[$i], $fileContents, $leaseId); + + // Dispose file contents + $fileContents = null; + unset($fileContents); + } + + // Close file + fclose($fp); + + // Put block list + $this->putBlockList($containerName, $blobName, $blockIdentifiers, $metadata, $leaseId); + + // Return information of the blob + return $this->getBlobInstance($containerName, $blobName, null, $leaseId); + } + + /** + * Put large blob block + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param string $identifier Block ID + * @param array $contents Contents of the block + * @param string $leaseId Lease identifier + * @throws Zend_Service_WindowsAzure_Exception + */ + public function putBlock($containerName = '', $blobName = '', $identifier = '', $contents = '', $leaseId = null) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($identifier === '') { + throw new Zend_Service_WindowsAzure_Exception('Block identifier is not specified.'); + } + if (strlen($contents) > self::MAX_BLOB_TRANSFER_SIZE) { + throw new Zend_Service_WindowsAzure_Exception('Block size is too big.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + + // Headers + $headers = array(); + if ($leaseId !== null) { + $headers['x-ms-lease-id'] = $leaseId; + } + + // Resource name + $resourceName = self::createResourceName($containerName , $blobName); + + // Upload + $response = $this->_performRequest($resourceName, '?comp=block&blockid=' . base64_encode($identifier), Zend_Http_Client::PUT, $headers, false, $contents, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if (!$response->isSuccessful()) { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Put block list + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param array $blockList Array of block identifiers + * @param array $metadata Key/value pairs of meta data + * @param string $leaseId Lease identifier + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. + * @throws Zend_Service_WindowsAzure_Exception + */ + public function putBlockList($containerName = '', $blobName = '', $blockList = array(), $metadata = array(), $leaseId = null, $additionalHeaders = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if (count($blockList) == 0) { + throw new Zend_Service_WindowsAzure_Exception('Block list does not contain any elements.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + + // Generate block list + $blocks = ''; + foreach ($blockList as $block) { + $blocks .= ' ' . base64_encode($block) . '' . "\n"; + } + + // Generate block list request + $fileContents = utf8_encode(implode("\n", array( + '', + '', + $blocks, + '' + ))); + + // Create metadata headers + $headers = array(); + if ($leaseId !== null) { + $headers['x-ms-lease-id'] = $leaseId; + } + $headers = array_merge($headers, $this->_generateMetadataHeaders($metadata)); + + // Additional headers? + foreach ($additionalHeaders as $key => $value) { + $headers[$key] = $value; + } + + // Resource name + $resourceName = self::createResourceName($containerName , $blobName); + + // Perform request + $response = $this->_performRequest($resourceName, '?comp=blocklist', Zend_Http_Client::PUT, $headers, false, $fileContents, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if (!$response->isSuccessful()) { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Get block list + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param string $snapshotId Snapshot identifier + * @param string $leaseId Lease identifier + * @param integer $type Type of block list to retrieve. 0 = all, 1 = committed, 2 = uncommitted + * @return array + * @throws Zend_Service_WindowsAzure_Exception + */ + public function getBlockList($containerName = '', $blobName = '', $snapshotId = null, $leaseId = null, $type = 0) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($type < 0 || $type > 2) { + throw new Zend_Service_WindowsAzure_Exception('Invalid type of block list to retrieve.'); + } + + // Set $blockListType + $blockListType = 'all'; + if ($type == 1) { + $blockListType = 'committed'; + } + if ($type == 2) { + $blockListType = 'uncommitted'; + } + + // Headers + $headers = array(); + if ($leaseId !== null) { + $headers['x-ms-lease-id'] = $leaseId; + } + + // Build query string + $queryString = array('comp=blocklist', 'blocklisttype=' . $blockListType); + if ($snapshotId !== null) { + $queryString[] = 'snapshot=' . $snapshotId; + } + $queryString = self::createQueryStringFromArray($queryString); + + // Resource name + $resourceName = self::createResourceName($containerName , $blobName); + + // Perform request + $response = $this->_performRequest($resourceName, $queryString, Zend_Http_Client::GET, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ); + if ($response->isSuccessful()) { + // Parse response + $blockList = $this->_parseResponse($response); + + // Create return value + $returnValue = array(); + if ($blockList->CommittedBlocks) { + foreach ($blockList->CommittedBlocks->Block as $block) { + $returnValue['CommittedBlocks'][] = (object)array( + 'Name' => (string)$block->Name, + 'Size' => (string)$block->Size + ); + } + } + if ($blockList->UncommittedBlocks) { + foreach ($blockList->UncommittedBlocks->Block as $block) { + $returnValue['UncommittedBlocks'][] = (object)array( + 'Name' => (string)$block->Name, + 'Size' => (string)$block->Size + ); + } + } + + return $returnValue; + } else { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Create page blob + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param int $size Size of the page blob in bytes + * @param array $metadata Key/value pairs of meta data + * @param string $leaseId Lease identifier + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. + * @return object Partial blob properties + * @throws Zend_Service_WindowsAzure_Exception + */ + public function createPageBlob($containerName = '', $blobName = '', $size = 0, $metadata = array(), $leaseId = null, $additionalHeaders = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + if ($size <= 0) { + throw new Zend_Service_WindowsAzure_Exception('Page blob size must be specified.'); + } + + // Create metadata headers + $headers = array(); + if ($leaseId !== null) { + $headers['x-ms-lease-id'] = $leaseId; + } + $headers = array_merge($headers, $this->_generateMetadataHeaders($metadata)); + + // Additional headers? + foreach ($additionalHeaders as $key => $value) { + $headers[$key] = $value; + } + + // Specify blob type & blob length + $headers[Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'blob-type'] = self::BLOBTYPE_PAGE; + $headers[Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'blob-content-length'] = $size; + $headers['Content-Length'] = 0; + + // Resource name + $resourceName = self::createResourceName($containerName , $blobName); + + // Perform request + $response = $this->_performRequest($resourceName, '', Zend_Http_Client::PUT, $headers, false, '', Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if ($response->isSuccessful()) { + return new Zend_Service_WindowsAzure_Storage_BlobInstance( + $containerName, + $blobName, + null, + $response->getHeader('Etag'), + $response->getHeader('Last-modified'), + $this->getBaseUrl() . '/' . $containerName . '/' . $blobName, + $size, + '', + '', + '', + false, + $metadata + ); + } else { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Put page in page blob + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param int $startByteOffset Start byte offset + * @param int $endByteOffset End byte offset + * @param mixed $contents Page contents + * @param string $writeMethod Write method (Zend_Service_WindowsAzure_Storage_Blob::PAGE_WRITE_*) + * @param string $leaseId Lease identifier + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. + * @throws Zend_Service_WindowsAzure_Exception + */ + public function putPage($containerName = '', $blobName = '', $startByteOffset = 0, $endByteOffset = 0, $contents = '', $writeMethod = self::PAGE_WRITE_UPDATE, $leaseId = null, $additionalHeaders = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + if ($startByteOffset % 512 != 0) { + throw new Zend_Service_WindowsAzure_Exception('Start byte offset must be a modulus of 512.'); + } + if (($endByteOffset + 1) % 512 != 0) { + throw new Zend_Service_WindowsAzure_Exception('End byte offset must be a modulus of 512 minus 1.'); + } + + // Determine size + $size = strlen($contents); + if ($size >= self::MAX_BLOB_TRANSFER_SIZE) { + throw new Zend_Service_WindowsAzure_Exception('Page blob size must not be larger than ' + self::MAX_BLOB_TRANSFER_SIZE . ' bytes.'); + } + + // Create metadata headers + $headers = array(); + if ($leaseId !== null) { + $headers['x-ms-lease-id'] = $leaseId; + } + + // Additional headers? + foreach ($additionalHeaders as $key => $value) { + $headers[$key] = $value; + } + + // Specify range + $headers['Range'] = 'bytes=' . $startByteOffset . '-' . $endByteOffset; + + // Write method + $headers[Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'page-write'] = $writeMethod; + + // Resource name + $resourceName = self::createResourceName($containerName , $blobName); + + // Perform request + $response = $this->_performRequest($resourceName, '?comp=page', Zend_Http_Client::PUT, $headers, false, $contents, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if (!$response->isSuccessful()) { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Put page in page blob + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param int $startByteOffset Start byte offset + * @param int $endByteOffset End byte offset + * @param string $leaseId Lease identifier + * @return array Array of page ranges + * @throws Zend_Service_WindowsAzure_Exception + */ + public function getPageRegions($containerName = '', $blobName = '', $startByteOffset = 0, $endByteOffset = 0, $leaseId = null) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + if ($startByteOffset % 512 != 0) { + throw new Zend_Service_WindowsAzure_Exception('Start byte offset must be a modulus of 512.'); + } + if ($endByteOffset > 0 && ($endByteOffset + 1) % 512 != 0) { + throw new Zend_Service_WindowsAzure_Exception('End byte offset must be a modulus of 512 minus 1.'); + } + + // Create metadata headers + $headers = array(); + if ($leaseId !== null) { + $headers['x-ms-lease-id'] = $leaseId; + } + + // Specify range? + if ($endByteOffset > 0) { + $headers['Range'] = 'bytes=' . $startByteOffset . '-' . $endByteOffset; + } + + // Resource name + $resourceName = self::createResourceName($containerName , $blobName); + + // Perform request + $response = $this->_performRequest($resourceName, '?comp=pagelist', Zend_Http_Client::GET, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if ($response->isSuccessful()) { + $result = $this->_parseResponse($response); + $xmlRanges = null; + if (count($result->PageRange) > 1) { + $xmlRanges = $result->PageRange; + } else { + $xmlRanges = array($result->PageRange); + } + + $ranges = array(); + for ($i = 0; $i < count($xmlRanges); $i++) { + $ranges[] = new Zend_Service_WindowsAzure_Storage_PageRegionInstance( + (int)$xmlRanges[$i]->Start, + (int)$xmlRanges[$i]->End + ); + } + + return $ranges; + } else { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Copy blob + * + * @param string $sourceContainerName Source container name + * @param string $sourceBlobName Source blob name + * @param string $destinationContainerName Destination container name + * @param string $destinationBlobName Destination blob name + * @param array $metadata Key/value pairs of meta data + * @param string $sourceSnapshotId Source snapshot identifier + * @param string $destinationLeaseId Destination lease identifier + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd894037.aspx for more information. + * @return object Partial blob properties + * @throws Zend_Service_WindowsAzure_Exception + */ + public function copyBlob($sourceContainerName = '', $sourceBlobName = '', $destinationContainerName = '', $destinationBlobName = '', $metadata = array(), $sourceSnapshotId = null, $destinationLeaseId = null, $additionalHeaders = array()) + { + if ($sourceContainerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Source container name is not specified.'); + } + if (!self::isValidContainerName($sourceContainerName)) { + throw new Zend_Service_WindowsAzure_Exception('Source container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($sourceBlobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Source blob name is not specified.'); + } + if ($destinationContainerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Destination container name is not specified.'); + } + if (!self::isValidContainerName($destinationContainerName)) { + throw new Zend_Service_WindowsAzure_Exception('Destination container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($destinationBlobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Destination blob name is not specified.'); + } + if ($sourceContainerName === '$root' && strpos($sourceBlobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + if ($destinationContainerName === '$root' && strpos($destinationBlobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + + // Create metadata headers + $headers = array(); + if ($destinationLeaseId !== null) { + $headers['x-ms-lease-id'] = $destinationLeaseId; + } + $headers = array_merge($headers, $this->_generateMetadataHeaders($metadata)); + + // Additional headers? + foreach ($additionalHeaders as $key => $value) { + $headers[$key] = $value; + } + + // Resource names + $sourceResourceName = self::createResourceName($sourceContainerName, $sourceBlobName); + if ($sourceSnapshotId !== null) { + $sourceResourceName .= '?snapshot=' . $sourceSnapshotId; + } + $destinationResourceName = self::createResourceName($destinationContainerName, $destinationBlobName); + + // Set source blob + $headers["x-ms-copy-source"] = '/' . $this->_accountName . '/' . $sourceResourceName; + + // Perform request + $response = $this->_performRequest($destinationResourceName, '', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if ($response->isSuccessful()) { + return new Zend_Service_WindowsAzure_Storage_BlobInstance( + $destinationContainerName, + $destinationBlobName, + null, + $response->getHeader('Etag'), + $response->getHeader('Last-modified'), + $this->getBaseUrl() . '/' . $destinationContainerName . '/' . $destinationBlobName, + 0, + '', + '', + '', + false, + $metadata + ); + } else { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Get blob + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param string $localFileName Local file name to store downloaded blob + * @param string $snapshotId Snapshot identifier + * @param string $leaseId Lease identifier + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. + * @throws Zend_Service_WindowsAzure_Exception + */ + public function getBlob($containerName = '', $blobName = '', $localFileName = '', $snapshotId = null, $leaseId = null, $additionalHeaders = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($localFileName === '') { + throw new Zend_Service_WindowsAzure_Exception('Local file name is not specified.'); + } + + // Fetch data + file_put_contents($localFileName, $this->getBlobData($containerName, $blobName, $snapshotId, $leaseId, $additionalHeaders)); + } + + /** + * Get blob data + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param string $snapshotId Snapshot identifier + * @param string $leaseId Lease identifier + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. + * @return mixed Blob contents + * @throws Zend_Service_WindowsAzure_Exception + */ + public function getBlobData($containerName = '', $blobName = '', $snapshotId = null, $leaseId = null, $additionalHeaders = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + + // Build query string + $queryString = array(); + if ($snapshotId !== null) { + $queryString[] = 'snapshot=' . $snapshotId; + } + $queryString = self::createQueryStringFromArray($queryString); + + // Additional headers? + $headers = array(); + if ($leaseId !== null) { + $headers['x-ms-lease-id'] = $leaseId; + } + foreach ($additionalHeaders as $key => $value) { + $headers[$key] = $value; + } + + // Resource name + $resourceName = self::createResourceName($containerName , $blobName); + + // Perform request + $response = $this->_performRequest($resourceName, $queryString, Zend_Http_Client::GET, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ); + if ($response->isSuccessful()) { + return $response->getBody(); + } else { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Get blob instance + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param string $snapshotId Snapshot identifier + * @param string $leaseId Lease identifier + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. + * @return Zend_Service_WindowsAzure_Storage_BlobInstance + * @throws Zend_Service_WindowsAzure_Exception + */ + public function getBlobInstance($containerName = '', $blobName = '', $snapshotId = null, $leaseId = null, $additionalHeaders = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + + // Build query string + $queryString = array(); + if ($snapshotId !== null) { + $queryString[] = 'snapshot=' . $snapshotId; + } + $queryString = self::createQueryStringFromArray($queryString); + + // Additional headers? + $headers = array(); + if ($leaseId !== null) { + $headers['x-ms-lease-id'] = $leaseId; + } + foreach ($additionalHeaders as $key => $value) { + $headers[$key] = $value; + } + + // Resource name + $resourceName = self::createResourceName($containerName , $blobName); + + // Perform request + $response = $this->_performRequest($resourceName, $queryString, Zend_Http_Client::HEAD, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ); + if ($response->isSuccessful()) { + // Parse metadata + $metadata = $this->_parseMetadataHeaders($response->getHeaders()); + + // Return blob + return new Zend_Service_WindowsAzure_Storage_BlobInstance( + $containerName, + $blobName, + $snapshotId, + $response->getHeader('Etag'), + $response->getHeader('Last-modified'), + $this->getBaseUrl() . '/' . $containerName . '/' . $blobName, + $response->getHeader('Content-Length'), + $response->getHeader('Content-Type'), + $response->getHeader('Content-Encoding'), + $response->getHeader('Content-Language'), + $response->getHeader('Cache-Control'), + $response->getHeader('x-ms-blob-type'), + $response->getHeader('x-ms-lease-status'), + false, + $metadata + ); + } else { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Get blob metadata + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param string $snapshotId Snapshot identifier + * @param string $leaseId Lease identifier + * @return array Key/value pairs of meta data + * @throws Zend_Service_WindowsAzure_Exception + */ + public function getBlobMetadata($containerName = '', $blobName = '', $snapshotId = null, $leaseId = null) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + + return $this->getBlobInstance($containerName, $blobName, $snapshotId, $leaseId)->Metadata; + } + + /** + * Set blob metadata + * + * Calling the Set Blob Metadata operation overwrites all existing metadata that is associated with the blob. It's not possible to modify an individual name/value pair. + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param array $metadata Key/value pairs of meta data + * @param string $leaseId Lease identifier + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. + * @throws Zend_Service_WindowsAzure_Exception + */ + public function setBlobMetadata($containerName = '', $blobName = '', $metadata = array(), $leaseId = null, $additionalHeaders = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + if (count($metadata) == 0) { + return; + } + + // Create metadata headers + $headers = array(); + if ($leaseId !== null) { + $headers['x-ms-lease-id'] = $leaseId; + } + $headers = array_merge($headers, $this->_generateMetadataHeaders($metadata)); + + // Additional headers? + foreach ($additionalHeaders as $key => $value) { + $headers[$key] = $value; + } + + // Perform request + $response = $this->_performRequest($containerName . '/' . $blobName, '?comp=metadata', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if (!$response->isSuccessful()) { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Set blob properties + * + * All available properties are listed at http://msdn.microsoft.com/en-us/library/ee691966.aspx and should be provided in the $additionalHeaders parameter. + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param string $leaseId Lease identifier + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. + * @throws Zend_Service_WindowsAzure_Exception + */ + public function setBlobProperties($containerName = '', $blobName = '', $leaseId = null, $additionalHeaders = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + if (count($additionalHeaders) == 0) { + throw new Zend_Service_WindowsAzure_Exception('No additional headers are specified.'); + } + + // Create headers + $headers = array(); + + // Lease set? + if ($leaseId !== null) { + $headers['x-ms-lease-id'] = $leaseId; + } + + // Additional headers? + foreach ($additionalHeaders as $key => $value) { + $headers[$key] = $value; + } + + // Perform request + $response = $this->_performRequest($containerName . '/' . $blobName, '?comp=properties', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if (!$response->isSuccessful()) { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Get blob properties + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param string $snapshotId Snapshot identifier + * @param string $leaseId Lease identifier + * @return Zend_Service_WindowsAzure_Storage_BlobInstance + * @throws Zend_Service_WindowsAzure_Exception + */ + public function getBlobProperties($containerName = '', $blobName = '', $snapshotId = null, $leaseId = null) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + + return $this->getBlobInstance($containerName, $blobName, $snapshotId, $leaseId); + } + + /** + * Delete blob + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param string $snapshotId Snapshot identifier + * @param string $leaseId Lease identifier + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. + * @throws Zend_Service_WindowsAzure_Exception + */ + public function deleteBlob($containerName = '', $blobName = '', $snapshotId = null, $leaseId = null, $additionalHeaders = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + + // Build query string + $queryString = array(); + if ($snapshotId !== null) { + $queryString[] = 'snapshot=' . $snapshotId; + } + $queryString = self::createQueryStringFromArray($queryString); + + // Additional headers? + $headers = array(); + if ($leaseId !== null) { + $headers['x-ms-lease-id'] = $leaseId; + } + foreach ($additionalHeaders as $key => $value) { + $headers[$key] = $value; + } + + // Resource name + $resourceName = self::createResourceName($containerName , $blobName); + + // Perform request + $response = $this->_performRequest($resourceName, $queryString, Zend_Http_Client::DELETE, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if (!$response->isSuccessful()) { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Snapshot blob + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param array $metadata Key/value pairs of meta data + * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. + * @return string Date/Time value representing the snapshot identifier. + * @throws Zend_Service_WindowsAzure_Exception + */ + public function snapshotBlob($containerName = '', $blobName = '', $metadata = array(), $additionalHeaders = array()) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + + // Additional headers? + $headers = array(); + foreach ($additionalHeaders as $key => $value) { + $headers[$key] = $value; + } + + // Resource name + $resourceName = self::createResourceName($containerName , $blobName); + + // Perform request + $response = $this->_performRequest($resourceName, '?comp=snapshot', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if ($response->isSuccessful()) { + return $response->getHeader('x-ms-snapshot'); + } else { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Lease blob - See (http://msdn.microsoft.com/en-us/library/ee691972.aspx) + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param string $leaseAction Lease action (Zend_Service_WindowsAzure_Storage_Blob::LEASE_*) + * @param string $leaseId Lease identifier, required to renew the lease or to release the lease. + * @return Zend_Service_WindowsAzure_Storage_LeaseInstance Lease instance + * @throws Zend_Service_WindowsAzure_Exception + */ + public function leaseBlob($containerName = '', $blobName = '', $leaseAction = self::LEASE_ACQUIRE, $leaseId = null) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + if ($blobName === '') { + throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.'); + } + if ($containerName === '$root' && strpos($blobName, '/') !== false) { + throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).'); + } + + // Additional headers? + $headers = array(); + $headers['x-ms-lease-action'] = strtolower($leaseAction); + if ($leaseId !== null) { + $headers['x-ms-lease-id'] = $leaseId; + } + + // Resource name + $resourceName = self::createResourceName($containerName , $blobName); + + // Perform request + $response = $this->_performRequest($resourceName, '?comp=lease', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE); + if ($response->isSuccessful()) { + return new Zend_Service_WindowsAzure_Storage_LeaseInstance( + $containerName, + $blobName, + $response->getHeader('x-ms-lease-id'), + $response->getHeader('x-ms-lease-time')); + } else { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * List blobs + * + * @param string $containerName Container name + * @param string $prefix Optional. Filters the results to return only blobs whose name begins with the specified prefix. + * @param string $delimiter Optional. Delimiter, i.e. '/', for specifying folder hierarchy + * @param int $maxResults Optional. Specifies the maximum number of blobs to return per call to Azure storage. This does NOT affect list size returned by this function. (maximum: 5000) + * @param string $marker Optional string value that identifies the portion of the list to be returned with the next list operation. + * @param string $include Optional. Specifies that the response should include one or more of the following subsets: '', 'metadata', 'snapshots', 'uncommittedblobs'). Multiple values can be added separated with a comma (,) + * @param int $currentResultCount Current result count (internal use) + * @return array + * @throws Zend_Service_WindowsAzure_Exception + */ + public function listBlobs($containerName = '', $prefix = '', $delimiter = '', $maxResults = null, $marker = null, $include = null, $currentResultCount = 0) + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + + // Build query string + $queryString = array('restype=container', 'comp=list'); + if ($prefix !== null) { + $queryString[] = 'prefix=' . $prefix; + } + if ($delimiter !== '') { + $queryString[] = 'delimiter=' . $delimiter; + } + if ($maxResults !== null) { + $queryString[] = 'maxresults=' . $maxResults; + } + if ($marker !== null) { + $queryString[] = 'marker=' . $marker; + } + if ($include !== null) { + $queryString[] = 'include=' . $include; + } + $queryString = self::createQueryStringFromArray($queryString); + + // Perform request + $response = $this->_performRequest($containerName, $queryString, Zend_Http_Client::GET, array(), false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_LIST); + if ($response->isSuccessful()) { + // Return value + $blobs = array(); + + // Blobs + $xmlBlobs = $this->_parseResponse($response)->Blobs->Blob; + if ($xmlBlobs !== null) { + for ($i = 0; $i < count($xmlBlobs); $i++) { + $properties = (array)$xmlBlobs[$i]->Properties; + + $blobs[] = new Zend_Service_WindowsAzure_Storage_BlobInstance( + $containerName, + (string)$xmlBlobs[$i]->Name, + (string)$xmlBlobs[$i]->Snapshot, + (string)$properties['Etag'], + (string)$properties['Last-Modified'], + (string)$xmlBlobs[$i]->Url, + (string)$properties['Content-Length'], + (string)$properties['Content-Type'], + (string)$properties['Content-Encoding'], + (string)$properties['Content-Language'], + (string)$properties['Cache-Control'], + (string)$properties['BlobType'], + (string)$properties['LeaseStatus'], + false, + $this->_parseMetadataElement($xmlBlobs[$i]) + ); + } + } + + // Blob prefixes (folders) + $xmlBlobs = $this->_parseResponse($response)->Blobs->BlobPrefix; + + if ($xmlBlobs !== null) { + for ($i = 0; $i < count($xmlBlobs); $i++) { + $blobs[] = new Zend_Service_WindowsAzure_Storage_BlobInstance( + $containerName, + (string)$xmlBlobs[$i]->Name, + null, + '', + '', + '', + 0, + '', + '', + '', + '', + '', + '', + true, + $this->_parseMetadataElement($xmlBlobs[$i]) + ); + } + } + + // More blobs? + $xmlMarker = (string)$this->_parseResponse($response)->NextMarker; + $currentResultCount = $currentResultCount + count($blobs); + if ($maxResults !== null && $currentResultCount < $maxResults) { + if ($xmlMarker !== null && $xmlMarker != '') { + $blobs = array_merge($blobs, $this->listBlobs($containerName, $prefix, $delimiter, $maxResults, $marker, $include, $currentResultCount)); + } + } + if ($maxResults !== null && count($blobs) > $maxResults) { + $blobs = array_slice($blobs, 0, $maxResults); + } + + return $blobs; + } else { + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Generate shared access URL + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @param string $resource Signed resource - container (c) - blob (b) + * @param string $permissions Signed permissions - read (r), write (w), delete (d) and list (l) + * @param string $start The time at which the Shared Access Signature becomes valid. + * @param string $expiry The time at which the Shared Access Signature becomes invalid. + * @param string $identifier Signed identifier + * @return string + */ + public function generateSharedAccessUrl($containerName = '', $blobName = '', $resource = 'b', $permissions = 'r', $start = '', $expiry = '', $identifier = '') + { + if ($containerName === '') { + throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.'); + } + if (!self::isValidContainerName($containerName)) { + throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.'); + } + + // Resource name + $resourceName = self::createResourceName($containerName , $blobName); + + // Generate URL + return $this->getBaseUrl() . '/' . $resourceName . '?' . + $this->_sharedAccessSignatureCredentials->createSignedQueryString( + $resourceName, + '', + $resource, + $permissions, + $start, + $expiry, + $identifier); + } + + /** + * Register this object as stream wrapper client + * + * @param string $name Protocol name + * @return Zend_Service_WindowsAzure_Storage_Blob + */ + public function registerAsClient($name) + { + self::$_wrapperClients[$name] = $this; + return $this; + } + + /** + * Unregister this object as stream wrapper client + * + * @param string $name Protocol name + * @return Zend_Service_WindowsAzure_Storage_Blob + */ + public function unregisterAsClient($name) + { + unset(self::$_wrapperClients[$name]); + return $this; + } + + /** + * Get wrapper client for stream type + * + * @param string $name Protocol name + * @return Zend_Service_WindowsAzure_Storage_Blob + */ + public static function getWrapperClient($name) + { + return self::$_wrapperClients[$name]; + } + + /** + * Register this object as stream wrapper + * + * @param string $name Protocol name + */ + public function registerStreamWrapper($name = 'azure') + { + /** + * @see Zend_Service_WindowsAzure_Storage_Blob_Stream + */ + require_once 'Zend/Service/WindowsAzure/Storage/Blob/Stream.php'; + + stream_register_wrapper($name, 'Zend_Service_WindowsAzure_Storage_Blob_Stream'); + $this->registerAsClient($name); + } + + /** + * Unregister this object as stream wrapper + * + * @param string $name Protocol name + * @return Zend_Service_WindowsAzure_Storage_Blob + */ + public function unregisterStreamWrapper($name = 'azure') + { + stream_wrapper_unregister($name); + $this->unregisterAsClient($name); + } + + /** + * Create resource name + * + * @param string $containerName Container name + * @param string $blobName Blob name + * @return string + */ + public static function createResourceName($containerName = '', $blobName = '') + { + // Resource name + $resourceName = $containerName . '/' . $blobName; + if ($containerName === '' || $containerName === '$root') { + $resourceName = $blobName; + } + if ($blobName === '') { + $resourceName = $containerName; + } + + return $resourceName; + } + + /** + * Is valid container name? + * + * @param string $containerName Container name + * @return boolean + */ + public static function isValidContainerName($containerName = '') + { + if ($containerName == '$root') { + return true; + } + + if (preg_match("/^[a-z0-9][a-z0-9-]*$/", $containerName) === 0) { + return false; + } + + if (strpos($containerName, '--') !== false) { + return false; + } + + if (strtolower($containerName) != $containerName) { + return false; + } + + if (strlen($containerName) < 3 || strlen($containerName) > 63) { + return false; + } + + if (substr($containerName, -1) == '-') { + return false; + } + + return true; + } + + /** + * Get error message from Zend_Http_Response + * + * @param Zend_Http_Response $response Repsonse + * @param string $alternativeError Alternative error message + * @return string + */ + protected function _getErrorMessage(Zend_Http_Response $response, $alternativeError = 'Unknown error.') + { + $response = $this->_parseResponse($response); + if ($response && $response->Message) { + return (string)$response->Message; + } else { + return $alternativeError; + } + } + + /** + * Generate block id + * + * @param int $part Block number + * @return string Windows Azure Blob Storage block number + */ + protected function _generateBlockId($part = 0) + { + $returnValue = $part; + while (strlen($returnValue) < 64) { + $returnValue = '0' . $returnValue; + } + + return $returnValue; + } +}