Blob.php 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Service_WindowsAzure
  17. * @subpackage Storage
  18. * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://todo name_todo
  20. * @version $Id: Blob.php 35835 2009-12-17 09:40:36Z unknown $
  21. */
  22. /**
  23. * @see Zend_Service_WindowsAzure_Credentials_SharedKey
  24. */
  25. require_once 'Zend/Service/WindowsAzure/Credentials/SharedKey.php';
  26. /**
  27. * @see Zend_Service_WindowsAzure_Credentials_SharedAccessSignature
  28. */
  29. require_once 'Zend/Service/WindowsAzure/Credentials/SharedAccessSignature.php';
  30. /**
  31. * @see Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract
  32. */
  33. require_once 'Zend/Service/WindowsAzure/RetryPolicy/RetryPolicyAbstract.php';
  34. /**
  35. * @see Zend_Http_Client
  36. */
  37. require_once 'Zend/Http/Client.php';
  38. /**
  39. * @see Zend_Http_Response
  40. */
  41. require_once 'Zend/Http/Response.php';
  42. /**
  43. * @see Zend_Service_WindowsAzure_Storage
  44. */
  45. require_once 'Zend/Service/WindowsAzure/Storage.php';
  46. /**
  47. * @see Zend_Service_WindowsAzure_Storage_BlobContainer
  48. */
  49. require_once 'Zend/Service/WindowsAzure/Storage/BlobContainer.php';
  50. /**
  51. * @see Zend_Service_WindowsAzure_Storage_BlobInstance
  52. */
  53. require_once 'Zend/Service/WindowsAzure/Storage/BlobInstance.php';
  54. /**
  55. * @see Zend_Service_WindowsAzure_Storage_SignedIdentifier
  56. */
  57. require_once 'Zend/Service/WindowsAzure/Storage/SignedIdentifier.php';
  58. /**
  59. * @see Zend_Service_WindowsAzure_Exception
  60. */
  61. require_once 'Zend/Service/WindowsAzure/Exception.php';
  62. /**
  63. * @category Zend
  64. * @package Zend_Service_WindowsAzure
  65. * @subpackage Storage
  66. * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
  67. * @license http://framework.zend.com/license/new-bsd New BSD License
  68. */
  69. class Zend_Service_WindowsAzure_Storage_Blob extends Zend_Service_WindowsAzure_Storage
  70. {
  71. /**
  72. * ACL - Private access
  73. */
  74. const ACL_PRIVATE = false;
  75. /**
  76. * ACL - Public access
  77. */
  78. const ACL_PUBLIC = true;
  79. /**
  80. * Maximal blob size (in bytes)
  81. */
  82. const MAX_BLOB_SIZE = 67108864;
  83. /**
  84. * Maximal blob transfer size (in bytes)
  85. */
  86. const MAX_BLOB_TRANSFER_SIZE = 4194304;
  87. /**
  88. * Stream wrapper clients
  89. *
  90. * @var array
  91. */
  92. protected static $_wrapperClients = array();
  93. /**
  94. * SharedAccessSignature credentials
  95. *
  96. * @var Zend_Service_WindowsAzure_Credentials_SharedAccessSignature
  97. */
  98. private $_sharedAccessSignatureCredentials = null;
  99. /**
  100. * Creates a new Zend_Service_WindowsAzure_Storage_Blob instance
  101. *
  102. * @param string $host Storage host name
  103. * @param string $accountName Account name for Windows Azure
  104. * @param string $accountKey Account key for Windows Azure
  105. * @param boolean $usePathStyleUri Use path-style URI's
  106. * @param Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy Retry policy to use when making requests
  107. */
  108. public function __construct($host = Zend_Service_WindowsAzure_Storage::URL_DEV_BLOB, $accountName = Zend_Service_WindowsAzure_Credentials_SharedKey::DEVSTORE_ACCOUNT, $accountKey = Zend_Service_WindowsAzure_Credentials_SharedKey::DEVSTORE_KEY, $usePathStyleUri = false, Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy = null)
  109. {
  110. parent::__construct($host, $accountName, $accountKey, $usePathStyleUri, $retryPolicy);
  111. // API version
  112. $this->_apiVersion = '2009-07-17';
  113. // SharedAccessSignature credentials
  114. $this->_sharedAccessSignatureCredentials = new Zend_Service_WindowsAzure_Credentials_SharedAccessSignature($accountName, $accountKey, $usePathStyleUri);
  115. }
  116. /**
  117. * Check if a blob exists
  118. *
  119. * @param string $containerName Container name
  120. * @param string $blobName Blob name
  121. * @return boolean
  122. */
  123. public function blobExists($containerName = '', $blobName = '')
  124. {
  125. if ($containerName === '') {
  126. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  127. }
  128. if (!self::isValidContainerName($containerName)) {
  129. 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.');
  130. }
  131. if ($blobName === '') {
  132. throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
  133. }
  134. // List blobs
  135. $blobs = $this->listBlobs($containerName, $blobName, '', 1);
  136. foreach ($blobs as $blob) {
  137. if ($blob->Name == $blobName) {
  138. return true;
  139. }
  140. }
  141. return false;
  142. }
  143. /**
  144. * Check if a container exists
  145. *
  146. * @param string $containerName Container name
  147. * @return boolean
  148. */
  149. public function containerExists($containerName = '')
  150. {
  151. if ($containerName === '') {
  152. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  153. }
  154. if (!self::isValidContainerName($containerName)) {
  155. 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.');
  156. }
  157. // List containers
  158. $containers = $this->listContainers($containerName, 1);
  159. foreach ($containers as $container) {
  160. if ($container->Name == $containerName) {
  161. return true;
  162. }
  163. }
  164. return false;
  165. }
  166. /**
  167. * Create container
  168. *
  169. * @param string $containerName Container name
  170. * @param array $metadata Key/value pairs of meta data
  171. * @return object Container properties
  172. * @throws Zend_Service_WindowsAzure_Exception
  173. */
  174. public function createContainer($containerName = '', $metadata = array())
  175. {
  176. if ($containerName === '') {
  177. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  178. }
  179. if (!self::isValidContainerName($containerName)) {
  180. 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.');
  181. }
  182. if (!is_array($metadata)) {
  183. throw new Zend_Service_WindowsAzure_Exception('Meta data should be an array of key and value pairs.');
  184. }
  185. // Create metadata headers
  186. $headers = array();
  187. foreach ($metadata as $key => $value) {
  188. $headers["x-ms-meta-" . strtolower($key)] = $value;
  189. }
  190. // Perform request
  191. $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);
  192. if ($response->isSuccessful()) {
  193. return new Zend_Service_WindowsAzure_Storage_BlobContainer(
  194. $containerName,
  195. $response->getHeader('Etag'),
  196. $response->getHeader('Last-modified'),
  197. $metadata
  198. );
  199. } else {
  200. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  201. }
  202. }
  203. /**
  204. * Get container ACL
  205. *
  206. * @param string $containerName Container name
  207. * @param bool $signedIdentifiers Display only public/private or display signed identifiers?
  208. * @return bool Acl, to be compared with Zend_Service_WindowsAzure_Storage_Blob::ACL_*
  209. * @throws Zend_Service_WindowsAzure_Exception
  210. */
  211. public function getContainerAcl($containerName = '', $signedIdentifiers = false)
  212. {
  213. if ($containerName === '') {
  214. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  215. }
  216. if (!self::isValidContainerName($containerName)) {
  217. 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.');
  218. }
  219. // Perform request
  220. $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);
  221. if ($response->isSuccessful()) {
  222. if ($signedIdentifiers == false) {
  223. // Only public/private
  224. return $response->getHeader('x-ms-prop-publicaccess') == 'True';
  225. } else {
  226. // Parse result
  227. $result = $this->_parseResponse($response);
  228. if (!$result) {
  229. return array();
  230. }
  231. $entries = null;
  232. if ($result->SignedIdentifier) {
  233. if (count($result->SignedIdentifier) > 1) {
  234. $entries = $result->SignedIdentifier;
  235. } else {
  236. $entries = array($result->SignedIdentifier);
  237. }
  238. }
  239. // Return value
  240. $returnValue = array();
  241. foreach ($entries as $entry) {
  242. $returnValue[] = new Zend_Service_WindowsAzure_Storage_SignedIdentifier(
  243. $entry->Id,
  244. $entry->AccessPolicy ? $entry->AccessPolicy->Start ? $entry->AccessPolicy->Start : '' : '',
  245. $entry->AccessPolicy ? $entry->AccessPolicy->Expiry ? $entry->AccessPolicy->Expiry : '' : '',
  246. $entry->AccessPolicy ? $entry->AccessPolicy->Permission ? $entry->AccessPolicy->Permission : '' : ''
  247. );
  248. }
  249. // Return
  250. return $returnValue;
  251. }
  252. } else {
  253. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  254. }
  255. }
  256. /**
  257. * Set container ACL
  258. *
  259. * @param string $containerName Container name
  260. * @param bool $acl Zend_Service_WindowsAzure_Storage_Blob::ACL_*
  261. * @param array $signedIdentifiers Signed identifiers
  262. * @throws Zend_Service_WindowsAzure_Exception
  263. */
  264. public function setContainerAcl($containerName = '', $acl = self::ACL_PRIVATE, $signedIdentifiers = array())
  265. {
  266. if ($containerName === '') {
  267. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  268. }
  269. if (!self::isValidContainerName($containerName)) {
  270. 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.');
  271. }
  272. // Policies
  273. $policies = null;
  274. if (is_array($signedIdentifiers) && count($signedIdentifiers) > 0) {
  275. $policies = '';
  276. $policies .= '<?xml version="1.0" encoding="utf-8"?>' . "\r\n";
  277. $policies .= '<SignedIdentifiers>' . "\r\n";
  278. foreach ($signedIdentifiers as $signedIdentifier) {
  279. $policies .= ' <SignedIdentifier>' . "\r\n";
  280. $policies .= ' <Id>' . $signedIdentifier->Id . '</Id>' . "\r\n";
  281. $policies .= ' <AccessPolicy>' . "\r\n";
  282. if ($signedIdentifier->Start != '')
  283. $policies .= ' <Start>' . $signedIdentifier->Start . '</Start>' . "\r\n";
  284. if ($signedIdentifier->Expiry != '')
  285. $policies .= ' <Expiry>' . $signedIdentifier->Expiry . '</Expiry>' . "\r\n";
  286. if ($signedIdentifier->Permissions != '')
  287. $policies .= ' <Permission>' . $signedIdentifier->Permissions . '</Permission>' . "\r\n";
  288. $policies .= ' </AccessPolicy>' . "\r\n";
  289. $policies .= ' </SignedIdentifier>' . "\r\n";
  290. }
  291. $policies .= '</SignedIdentifiers>' . "\r\n";
  292. }
  293. // Perform request
  294. $response = $this->_performRequest($containerName, '?restype=container&comp=acl', Zend_Http_Client::PUT, array('x-ms-prop-publicaccess' => $acl), false, $policies, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
  295. if (!$response->isSuccessful()) {
  296. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  297. }
  298. }
  299. /**
  300. * Get container
  301. *
  302. * @param string $containerName Container name
  303. * @return Zend_Service_WindowsAzure_Storage_BlobContainer
  304. * @throws Zend_Service_WindowsAzure_Exception
  305. */
  306. public function getContainer($containerName = '')
  307. {
  308. if ($containerName === '') {
  309. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  310. }
  311. if (!self::isValidContainerName($containerName)) {
  312. 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.');
  313. }
  314. // Perform request
  315. $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);
  316. if ($response->isSuccessful()) {
  317. // Parse metadata
  318. $metadata = array();
  319. foreach ($response->getHeaders() as $key => $value) {
  320. if (substr(strtolower($key), 0, 10) == "x-ms-meta-") {
  321. $metadata[str_replace("x-ms-meta-", '', strtolower($key))] = $value;
  322. }
  323. }
  324. // Return container
  325. return new Zend_Service_WindowsAzure_Storage_BlobContainer(
  326. $containerName,
  327. $response->getHeader('Etag'),
  328. $response->getHeader('Last-modified'),
  329. $metadata
  330. );
  331. } else {
  332. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  333. }
  334. }
  335. /**
  336. * Get container metadata
  337. *
  338. * @param string $containerName Container name
  339. * @return array Key/value pairs of meta data
  340. * @throws Zend_Service_WindowsAzure_Exception
  341. */
  342. public function getContainerMetadata($containerName = '')
  343. {
  344. if ($containerName === '') {
  345. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  346. }
  347. if (!self::isValidContainerName($containerName)) {
  348. 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.');
  349. }
  350. return $this->getContainer($containerName)->Metadata;
  351. }
  352. /**
  353. * Set container metadata
  354. *
  355. * 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.
  356. *
  357. * @param string $containerName Container name
  358. * @param array $metadata Key/value pairs of meta data
  359. * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
  360. * @throws Zend_Service_WindowsAzure_Exception
  361. */
  362. public function setContainerMetadata($containerName = '', $metadata = array(), $additionalHeaders = array())
  363. {
  364. if ($containerName === '') {
  365. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  366. }
  367. if (!self::isValidContainerName($containerName)) {
  368. 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.');
  369. }
  370. if (!is_array($metadata)) {
  371. throw new Zend_Service_WindowsAzure_Exception('Meta data should be an array of key and value pairs.');
  372. }
  373. if (count($metadata) == 0) {
  374. return;
  375. }
  376. // Create metadata headers
  377. $headers = array();
  378. foreach ($metadata as $key => $value) {
  379. $headers["x-ms-meta-" . strtolower($key)] = $value;
  380. }
  381. // Additional headers?
  382. foreach ($additionalHeaders as $key => $value) {
  383. $headers[$key] = $value;
  384. }
  385. // Perform request
  386. $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);
  387. if (!$response->isSuccessful()) {
  388. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  389. }
  390. }
  391. /**
  392. * Delete container
  393. *
  394. * @param string $containerName Container name
  395. * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
  396. * @throws Zend_Service_WindowsAzure_Exception
  397. */
  398. public function deleteContainer($containerName = '', $additionalHeaders = array())
  399. {
  400. if ($containerName === '') {
  401. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  402. }
  403. if (!self::isValidContainerName($containerName)) {
  404. 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.');
  405. }
  406. // Additional headers?
  407. $headers = array();
  408. foreach ($additionalHeaders as $key => $value) {
  409. $headers[$key] = $value;
  410. }
  411. // Perform request
  412. $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);
  413. if (!$response->isSuccessful()) {
  414. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  415. }
  416. }
  417. /**
  418. * List containers
  419. *
  420. * @param string $prefix Optional. Filters the results to return only containers whose name begins with the specified prefix.
  421. * @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)
  422. * @param string $marker Optional string value that identifies the portion of the list to be returned with the next list operation.
  423. * @param int $currentResultCount Current result count (internal use)
  424. * @return array
  425. * @throws Zend_Service_WindowsAzure_Exception
  426. */
  427. public function listContainers($prefix = null, $maxResults = null, $marker = null, $currentResultCount = 0)
  428. {
  429. // Build query string
  430. $queryString = '?comp=list';
  431. if (!is_null($prefix)) {
  432. $queryString .= '&prefix=' . $prefix;
  433. }
  434. if (!is_null($maxResults)) {
  435. $queryString .= '&maxresults=' . $maxResults;
  436. }
  437. if (!is_null($marker)) {
  438. $queryString .= '&marker=' . $marker;
  439. }
  440. // Perform request
  441. $response = $this->_performRequest('', $queryString, Zend_Http_Client::GET, array(), false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_LIST);
  442. if ($response->isSuccessful()) {
  443. $xmlContainers = $this->_parseResponse($response)->Containers->Container;
  444. $xmlMarker = (string) $this->_parseResponse($response)->NextMarker;
  445. $containers = array();
  446. if (!is_null($xmlContainers)) {
  447. for ($i = 0; $i < count($xmlContainers); $i++) {
  448. $containers[] = new Zend_Service_WindowsAzure_Storage_BlobContainer(
  449. (string)$xmlContainers[$i]->Name,
  450. (string)$xmlContainers[$i]->Etag,
  451. (string)$xmlContainers[$i]->LastModified
  452. );
  453. }
  454. }
  455. $currentResultCount = $currentResultCount + count($containers);
  456. if (!is_null($maxResults) && $currentResultCount < $maxResults) {
  457. if (!is_null($xmlMarker) && $xmlMarker != '') {
  458. $containers = array_merge($containers, $this->listContainers($prefix, $maxResults, $xmlMarker, $currentResultCount));
  459. }
  460. }
  461. if (!is_null($maxResults) && count($containers) > $maxResults) {
  462. $containers = array_slice($containers, 0, $maxResults);
  463. }
  464. return $containers;
  465. } else {
  466. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  467. }
  468. }
  469. /**
  470. * Put blob
  471. *
  472. * @param string $containerName Container name
  473. * @param string $blobName Blob name
  474. * @param string $localFileName Local file name to be uploaded
  475. * @param array $metadata Key/value pairs of meta data
  476. * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
  477. * @return object Partial blob properties
  478. * @throws Zend_Service_WindowsAzure_Exception
  479. */
  480. public function putBlob($containerName = '', $blobName = '', $localFileName = '', $metadata = array(), $additionalHeaders = array())
  481. {
  482. if ($containerName === '') {
  483. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  484. }
  485. if (!self::isValidContainerName($containerName)) {
  486. 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.');
  487. }
  488. if ($blobName === '') {
  489. throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
  490. }
  491. if ($localFileName === '') {
  492. throw new Zend_Service_WindowsAzure_Exception('Local file name is not specified.');
  493. }
  494. if (!file_exists($localFileName)) {
  495. throw new Zend_Service_WindowsAzure_Exception('Local file not found.');
  496. }
  497. if ($containerName === '$root' && strpos($blobName, '/') !== false) {
  498. throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
  499. }
  500. // Check file size
  501. if (filesize($localFileName) >= self::MAX_BLOB_SIZE) {
  502. return $this->putLargeBlob($containerName, $blobName, $localFileName, $metadata);
  503. }
  504. // Create metadata headers
  505. $headers = array();
  506. foreach ($metadata as $key => $value) {
  507. $headers["x-ms-meta-" . strtolower($key)] = $value;
  508. }
  509. // Additional headers?
  510. foreach ($additionalHeaders as $key => $value) {
  511. $headers[$key] = $value;
  512. }
  513. // File contents
  514. $fileContents = file_get_contents($localFileName);
  515. // Resource name
  516. $resourceName = self::createResourceName($containerName , $blobName);
  517. // Perform request
  518. $response = $this->_performRequest($resourceName, '', Zend_Http_Client::PUT, $headers, false, $fileContents, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
  519. if ($response->isSuccessful()) {
  520. return new Zend_Service_WindowsAzure_Storage_BlobInstance(
  521. $containerName,
  522. $blobName,
  523. $response->getHeader('Etag'),
  524. $response->getHeader('Last-modified'),
  525. $this->getBaseUrl() . '/' . $containerName . '/' . $blobName,
  526. strlen($fileContents),
  527. '',
  528. '',
  529. '',
  530. false,
  531. $metadata
  532. );
  533. } else {
  534. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  535. }
  536. }
  537. /**
  538. * Put large blob (> 64 MB)
  539. *
  540. * @param string $containerName Container name
  541. * @param string $blobName Blob name
  542. * @param string $localFileName Local file name to be uploaded
  543. * @param array $metadata Key/value pairs of meta data
  544. * @return object Partial blob properties
  545. * @throws Zend_Service_WindowsAzure_Exception
  546. */
  547. public function putLargeBlob($containerName = '', $blobName = '', $localFileName = '', $metadata = array())
  548. {
  549. if ($containerName === '') {
  550. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  551. }
  552. if (!self::isValidContainerName($containerName)) {
  553. 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.');
  554. }
  555. if ($blobName === '') {
  556. throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
  557. }
  558. if ($localFileName === '') {
  559. throw new Zend_Service_WindowsAzure_Exception('Local file name is not specified.');
  560. }
  561. if (!file_exists($localFileName)) {
  562. throw new Zend_Service_WindowsAzure_Exception('Local file not found.');
  563. }
  564. if ($containerName === '$root' && strpos($blobName, '/') !== false) {
  565. throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
  566. }
  567. // Check file size
  568. if (filesize($localFileName) < self::MAX_BLOB_SIZE) {
  569. return $this->putBlob($containerName, $blobName, $localFileName, $metadata);
  570. }
  571. // Determine number of parts
  572. $numberOfParts = ceil( filesize($localFileName) / self::MAX_BLOB_TRANSFER_SIZE );
  573. // Generate block id's
  574. $blockIdentifiers = array();
  575. for ($i = 0; $i < $numberOfParts; $i++) {
  576. $blockIdentifiers[] = $this->_generateBlockId($i);
  577. }
  578. // Open file
  579. $fp = fopen($localFileName, 'r');
  580. if ($fp === false) {
  581. throw new Zend_Service_WindowsAzure_Exception('Could not open local file.');
  582. }
  583. // Upload parts
  584. for ($i = 0; $i < $numberOfParts; $i++) {
  585. // Seek position in file
  586. fseek($fp, $i * self::MAX_BLOB_TRANSFER_SIZE);
  587. // Read contents
  588. $fileContents = fread($fp, self::MAX_BLOB_TRANSFER_SIZE);
  589. // Put block
  590. $this->putBlock($containerName, $blobName, $blockIdentifiers[$i], $fileContents);
  591. // Dispose file contents
  592. $fileContents = null;
  593. unset($fileContents);
  594. }
  595. // Close file
  596. fclose($fp);
  597. // Put block list
  598. $this->putBlockList($containerName, $blobName, $blockIdentifiers, $metadata);
  599. // Return information of the blob
  600. return $this->getBlobInstance($containerName, $blobName);
  601. }
  602. /**
  603. * Put large blob block
  604. *
  605. * @param string $containerName Container name
  606. * @param string $blobName Blob name
  607. * @param string $identifier Block ID
  608. * @param array $contents Contents of the block
  609. * @throws Zend_Service_WindowsAzure_Exception
  610. */
  611. public function putBlock($containerName = '', $blobName = '', $identifier = '', $contents = '')
  612. {
  613. if ($containerName === '') {
  614. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  615. }
  616. if (!self::isValidContainerName($containerName)) {
  617. 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.');
  618. }
  619. if ($identifier === '') {
  620. throw new Zend_Service_WindowsAzure_Exception('Block identifier is not specified.');
  621. }
  622. if (strlen($contents) > self::MAX_BLOB_TRANSFER_SIZE) {
  623. throw new Zend_Service_WindowsAzure_Exception('Block size is too big.');
  624. }
  625. if ($containerName === '$root' && strpos($blobName, '/') !== false) {
  626. throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
  627. }
  628. // Resource name
  629. $resourceName = self::createResourceName($containerName , $blobName);
  630. // Upload
  631. $response = $this->_performRequest($resourceName, '?comp=block&blockid=' . base64_encode($identifier), Zend_Http_Client::PUT, null, false, $contents, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
  632. if (!$response->isSuccessful()) {
  633. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  634. }
  635. }
  636. /**
  637. * Put block list
  638. *
  639. * @param string $containerName Container name
  640. * @param string $blobName Blob name
  641. * @param array $blockList Array of block identifiers
  642. * @param array $metadata Key/value pairs of meta data
  643. * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
  644. * @throws Zend_Service_WindowsAzure_Exception
  645. */
  646. public function putBlockList($containerName = '', $blobName = '', $blockList = array(), $metadata = array(), $additionalHeaders = array())
  647. {
  648. if ($containerName === '') {
  649. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  650. }
  651. if (!self::isValidContainerName($containerName)) {
  652. 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.');
  653. }
  654. if ($blobName === '') {
  655. throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
  656. }
  657. if (count($blockList) == 0) {
  658. throw new Zend_Service_WindowsAzure_Exception('Block list does not contain any elements.');
  659. }
  660. if ($containerName === '$root' && strpos($blobName, '/') !== false) {
  661. throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
  662. }
  663. // Generate block list
  664. $blocks = '';
  665. foreach ($blockList as $block) {
  666. $blocks .= ' <Latest>' . base64_encode($block) . '</Latest>' . "\n";
  667. }
  668. // Generate block list request
  669. $fileContents = utf8_encode(implode("\n", array(
  670. '<?xml version="1.0" encoding="utf-8"?>',
  671. '<BlockList>',
  672. $blocks,
  673. '</BlockList>'
  674. )));
  675. // Create metadata headers
  676. $headers = array();
  677. foreach ($metadata as $key => $value) {
  678. $headers["x-ms-meta-" . strtolower($key)] = $value;
  679. }
  680. // Additional headers?
  681. foreach ($additionalHeaders as $key => $value) {
  682. $headers[$key] = $value;
  683. }
  684. // Resource name
  685. $resourceName = self::createResourceName($containerName , $blobName);
  686. // Perform request
  687. $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);
  688. if (!$response->isSuccessful()) {
  689. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  690. }
  691. }
  692. /**
  693. * Get block list
  694. *
  695. * @param string $containerName Container name
  696. * @param string $blobName Blob name
  697. * @param integer $type Type of block list to retrieve. 0 = all, 1 = committed, 2 = uncommitted
  698. * @return array
  699. * @throws Zend_Service_WindowsAzure_Exception
  700. */
  701. public function getBlockList($containerName = '', $blobName = '', $type = 0)
  702. {
  703. if ($containerName === '') {
  704. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  705. }
  706. if (!self::isValidContainerName($containerName)) {
  707. 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.');
  708. }
  709. if ($blobName === '') {
  710. throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
  711. }
  712. if ($type < 0 || $type > 2) {
  713. throw new Zend_Service_WindowsAzure_Exception('Invalid type of block list to retrieve.');
  714. }
  715. // Set $blockListType
  716. $blockListType = 'all';
  717. if ($type == 1) {
  718. $blockListType = 'committed';
  719. }
  720. if ($type == 2) {
  721. $blockListType = 'uncommitted';
  722. }
  723. // Resource name
  724. $resourceName = self::createResourceName($containerName , $blobName);
  725. // Perform request
  726. $response = $this->_performRequest($resourceName, '?comp=blocklist&blocklisttype=' . $blockListType, Zend_Http_Client::GET, array(), false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ);
  727. if ($response->isSuccessful()) {
  728. // Parse response
  729. $blockList = $this->_parseResponse($response);
  730. // Create return value
  731. $returnValue = array();
  732. if ($blockList->CommittedBlocks) {
  733. foreach ($blockList->CommittedBlocks->Block as $block) {
  734. $returnValue['CommittedBlocks'][] = (object)array(
  735. 'Name' => (string)$block->Name,
  736. 'Size' => (string)$block->Size
  737. );
  738. }
  739. }
  740. if ($blockList->UncommittedBlocks) {
  741. foreach ($blockList->UncommittedBlocks->Block as $block) {
  742. $returnValue['UncommittedBlocks'][] = (object)array(
  743. 'Name' => (string)$block->Name,
  744. 'Size' => (string)$block->Size
  745. );
  746. }
  747. }
  748. return $returnValue;
  749. } else {
  750. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  751. }
  752. }
  753. /**
  754. * Copy blob
  755. *
  756. * @param string $sourceContainerName Source container name
  757. * @param string $sourceBlobName Source blob name
  758. * @param string $destinationContainerName Destination container name
  759. * @param string $destinationBlobName Destination blob name
  760. * @param array $metadata Key/value pairs of meta data
  761. * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd894037.aspx for more information.
  762. * @return object Partial blob properties
  763. * @throws Zend_Service_WindowsAzure_Exception
  764. */
  765. public function copyBlob($sourceContainerName = '', $sourceBlobName = '', $destinationContainerName = '', $destinationBlobName = '', $metadata = array(), $additionalHeaders = array())
  766. {
  767. if ($sourceContainerName === '') {
  768. throw new Zend_Service_WindowsAzure_Exception('Source container name is not specified.');
  769. }
  770. if (!self::isValidContainerName($sourceContainerName)) {
  771. 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.');
  772. }
  773. if ($sourceBlobName === '') {
  774. throw new Zend_Service_WindowsAzure_Exception('Source blob name is not specified.');
  775. }
  776. if ($destinationContainerName === '') {
  777. throw new Zend_Service_WindowsAzure_Exception('Destination container name is not specified.');
  778. }
  779. if (!self::isValidContainerName($destinationContainerName)) {
  780. 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.');
  781. }
  782. if ($destinationBlobName === '') {
  783. throw new Zend_Service_WindowsAzure_Exception('Destination blob name is not specified.');
  784. }
  785. if ($sourceContainerName === '$root' && strpos($sourceBlobName, '/') !== false) {
  786. throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
  787. }
  788. if ($destinationContainerName === '$root' && strpos($destinationBlobName, '/') !== false) {
  789. throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
  790. }
  791. // Create metadata headers
  792. $headers = array();
  793. foreach ($metadata as $key => $value) {
  794. $headers["x-ms-meta-" . strtolower($key)] = $value;
  795. }
  796. // Additional headers?
  797. foreach ($additionalHeaders as $key => $value) {
  798. $headers[$key] = $value;
  799. }
  800. // Resource names
  801. $sourceResourceName = self::createResourceName($sourceContainerName, $sourceBlobName);
  802. $destinationResourceName = self::createResourceName($destinationContainerName, $destinationBlobName);
  803. // Set source blob
  804. $headers["x-ms-copy-source"] = '/' . $this->_accountName . '/' . $sourceResourceName;
  805. // Perform request
  806. $response = $this->_performRequest($destinationResourceName, '', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
  807. if ($response->isSuccessful()) {
  808. return new Zend_Service_WindowsAzure_Storage_BlobInstance(
  809. $destinationContainerName,
  810. $destinationBlobName,
  811. $response->getHeader('Etag'),
  812. $response->getHeader('Last-modified'),
  813. $this->getBaseUrl() . '/' . $destinationContainerName . '/' . $destinationBlobName,
  814. 0,
  815. '',
  816. '',
  817. '',
  818. false,
  819. $metadata
  820. );
  821. } else {
  822. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  823. }
  824. }
  825. /**
  826. * Get blob
  827. *
  828. * @param string $containerName Container name
  829. * @param string $blobName Blob name
  830. * @param string $localFileName Local file name to store downloaded blob
  831. * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
  832. * @throws Zend_Service_WindowsAzure_Exception
  833. */
  834. public function getBlob($containerName = '', $blobName = '', $localFileName = '', $additionalHeaders = array())
  835. {
  836. if ($containerName === '') {
  837. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  838. }
  839. if (!self::isValidContainerName($containerName)) {
  840. 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.');
  841. }
  842. if ($blobName === '') {
  843. throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
  844. }
  845. if ($localFileName === '') {
  846. throw new Zend_Service_WindowsAzure_Exception('Local file name is not specified.');
  847. }
  848. // Additional headers?
  849. $headers = array();
  850. foreach ($additionalHeaders as $key => $value) {
  851. $headers[$key] = $value;
  852. }
  853. // Resource name
  854. $resourceName = self::createResourceName($containerName , $blobName);
  855. // Perform request
  856. $response = $this->_performRequest($resourceName, '', Zend_Http_Client::GET, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ);
  857. if ($response->isSuccessful()) {
  858. file_put_contents($localFileName, $response->getBody());
  859. } else {
  860. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  861. }
  862. }
  863. /**
  864. * Get container
  865. *
  866. * @param string $containerName Container name
  867. * @param string $blobName Blob name
  868. * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
  869. * @return Zend_Service_WindowsAzure_Storage_BlobInstance
  870. * @throws Zend_Service_WindowsAzure_Exception
  871. */
  872. public function getBlobInstance($containerName = '', $blobName = '', $additionalHeaders = array())
  873. {
  874. if ($containerName === '') {
  875. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  876. }
  877. if (!self::isValidContainerName($containerName)) {
  878. 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.');
  879. }
  880. if ($blobName === '') {
  881. throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
  882. }
  883. if ($containerName === '$root' && strpos($blobName, '/') !== false) {
  884. throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
  885. }
  886. // Additional headers?
  887. $headers = array();
  888. foreach ($additionalHeaders as $key => $value) {
  889. $headers[$key] = $value;
  890. }
  891. // Resource name
  892. $resourceName = self::createResourceName($containerName , $blobName);
  893. // Perform request
  894. $response = $this->_performRequest($resourceName, '', Zend_Http_Client::HEAD, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ);
  895. if ($response->isSuccessful()) {
  896. // Parse metadata
  897. $metadata = array();
  898. foreach ($response->getHeaders() as $key => $value) {
  899. if (substr(strtolower($key), 0, 10) == "x-ms-meta-") {
  900. $metadata[str_replace("x-ms-meta-", '', strtolower($key))] = $value;
  901. }
  902. }
  903. // Return blob
  904. return new Zend_Service_WindowsAzure_Storage_BlobInstance(
  905. $containerName,
  906. $blobName,
  907. $response->getHeader('Etag'),
  908. $response->getHeader('Last-modified'),
  909. $this->getBaseUrl() . '/' . $containerName . '/' . $blobName,
  910. $response->getHeader('Content-Length'),
  911. $response->getHeader('Content-Type'),
  912. $response->getHeader('Content-Encoding'),
  913. $response->getHeader('Content-Language'),
  914. false,
  915. $metadata
  916. );
  917. } else {
  918. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  919. }
  920. }
  921. /**
  922. * Get blob metadata
  923. *
  924. * @param string $containerName Container name
  925. * @param string $blobName Blob name
  926. * @return array Key/value pairs of meta data
  927. * @throws Zend_Service_WindowsAzure_Exception
  928. */
  929. public function getBlobMetadata($containerName = '', $blobName = '')
  930. {
  931. if ($containerName === '') {
  932. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  933. }
  934. if (!self::isValidContainerName($containerName)) {
  935. 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.');
  936. }
  937. if ($blobName === '') {
  938. throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
  939. }
  940. if ($containerName === '$root' && strpos($blobName, '/') !== false) {
  941. throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
  942. }
  943. return $this->getBlobInstance($containerName, $blobName)->Metadata;
  944. }
  945. /**
  946. * Set blob metadata
  947. *
  948. * 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.
  949. *
  950. * @param string $containerName Container name
  951. * @param string $blobName Blob name
  952. * @param array $metadata Key/value pairs of meta data
  953. * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
  954. * @throws Zend_Service_WindowsAzure_Exception
  955. */
  956. public function setBlobMetadata($containerName = '', $blobName = '', $metadata = array(), $additionalHeaders = array())
  957. {
  958. if ($containerName === '') {
  959. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  960. }
  961. if (!self::isValidContainerName($containerName)) {
  962. 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.');
  963. }
  964. if ($blobName === '') {
  965. throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
  966. }
  967. if ($containerName === '$root' && strpos($blobName, '/') !== false) {
  968. throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
  969. }
  970. if (count($metadata) == 0) {
  971. return;
  972. }
  973. // Create metadata headers
  974. $headers = array();
  975. foreach ($metadata as $key => $value) {
  976. $headers["x-ms-meta-" . strtolower($key)] = $value;
  977. }
  978. // Additional headers?
  979. foreach ($additionalHeaders as $key => $value) {
  980. $headers[$key] = $value;
  981. }
  982. // Perform request
  983. $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);
  984. if (!$response->isSuccessful()) {
  985. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  986. }
  987. }
  988. /**
  989. * Delete blob
  990. *
  991. * @param string $containerName Container name
  992. * @param string $blobName Blob name
  993. * @param array $additionalHeaders Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
  994. * @throws Zend_Service_WindowsAzure_Exception
  995. */
  996. public function deleteBlob($containerName = '', $blobName = '', $additionalHeaders = array())
  997. {
  998. if ($containerName === '') {
  999. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  1000. }
  1001. if (!self::isValidContainerName($containerName)) {
  1002. 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.');
  1003. }
  1004. if ($blobName === '') {
  1005. throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
  1006. }
  1007. if ($containerName === '$root' && strpos($blobName, '/') !== false) {
  1008. throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
  1009. }
  1010. // Additional headers?
  1011. $headers = array();
  1012. foreach ($additionalHeaders as $key => $value) {
  1013. $headers[$key] = $value;
  1014. }
  1015. // Resource name
  1016. $resourceName = self::createResourceName($containerName , $blobName);
  1017. // Perform request
  1018. $response = $this->_performRequest($resourceName, '', Zend_Http_Client::DELETE, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
  1019. if (!$response->isSuccessful()) {
  1020. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  1021. }
  1022. }
  1023. /**
  1024. * List blobs
  1025. *
  1026. * @param string $containerName Container name
  1027. * @param string $prefix Optional. Filters the results to return only blobs whose name begins with the specified prefix.
  1028. * @param string $delimiter Optional. Delimiter, i.e. '/', for specifying folder hierarchy
  1029. * @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)
  1030. * @param string $marker Optional string value that identifies the portion of the list to be returned with the next list operation.
  1031. * @param int $currentResultCount Current result count (internal use)
  1032. * @return array
  1033. * @throws Zend_Service_WindowsAzure_Exception
  1034. */
  1035. public function listBlobs($containerName = '', $prefix = '', $delimiter = '', $maxResults = null, $marker = null, $currentResultCount = 0)
  1036. {
  1037. if ($containerName === '') {
  1038. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  1039. }
  1040. if (!self::isValidContainerName($containerName)) {
  1041. 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.');
  1042. }
  1043. // Build query string
  1044. $queryString = '?restype=container&comp=list';
  1045. if (!is_null($prefix)) {
  1046. $queryString .= '&prefix=' . $prefix;
  1047. }
  1048. if ($delimiter !== '') {
  1049. $queryString .= '&delimiter=' . $delimiter;
  1050. }
  1051. if (!is_null($maxResults)) {
  1052. $queryString .= '&maxresults=' . $maxResults;
  1053. }
  1054. if (!is_null($marker)) {
  1055. $queryString .= '&marker=' . $marker;
  1056. }
  1057. // Perform request
  1058. $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);
  1059. if ($response->isSuccessful()) {
  1060. // Return value
  1061. $blobs = array();
  1062. // Blobs
  1063. $xmlBlobs = $this->_parseResponse($response)->Blobs->Blob;
  1064. if (!is_null($xmlBlobs)) {
  1065. for ($i = 0; $i < count($xmlBlobs); $i++) {
  1066. $blobs[] = new Zend_Service_WindowsAzure_Storage_BlobInstance(
  1067. $containerName,
  1068. (string)$xmlBlobs[$i]->Name,
  1069. (string)$xmlBlobs[$i]->Etag,
  1070. (string)$xmlBlobs[$i]->LastModified,
  1071. (string)$xmlBlobs[$i]->Url,
  1072. (string)$xmlBlobs[$i]->Size,
  1073. (string)$xmlBlobs[$i]->ContentType,
  1074. (string)$xmlBlobs[$i]->ContentEncoding,
  1075. (string)$xmlBlobs[$i]->ContentLanguage,
  1076. false
  1077. );
  1078. }
  1079. }
  1080. // Blob prefixes (folders)
  1081. $xmlBlobs = $this->_parseResponse($response)->Blobs->BlobPrefix;
  1082. if (!is_null($xmlBlobs)) {
  1083. for ($i = 0; $i < count($xmlBlobs); $i++) {
  1084. $blobs[] = new Zend_Service_WindowsAzure_Storage_BlobInstance(
  1085. $containerName,
  1086. (string)$xmlBlobs[$i]->Name,
  1087. '',
  1088. '',
  1089. '',
  1090. 0,
  1091. '',
  1092. '',
  1093. '',
  1094. true
  1095. );
  1096. }
  1097. }
  1098. // More blobs?
  1099. $xmlMarker = (string)$this->_parseResponse($response)->NextMarker;
  1100. $currentResultCount = $currentResultCount + count($blobs);
  1101. if (!is_null($maxResults) && $currentResultCount < $maxResults) {
  1102. if (!is_null($xmlMarker) && $xmlMarker != '') {
  1103. $blobs = array_merge($blobs, $this->listBlobs($containerName, $prefix, $delimiter, $maxResults, $marker, $currentResultCount));
  1104. }
  1105. }
  1106. if (!is_null($maxResults) && count($blobs) > $maxResults) {
  1107. $blobs = array_slice($blobs, 0, $maxResults);
  1108. }
  1109. return $blobs;
  1110. } else {
  1111. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  1112. }
  1113. }
  1114. /**
  1115. * Generate shared access URL
  1116. *
  1117. * @param string $containerName Container name
  1118. * @param string $blobName Blob name
  1119. * @param string $resource Signed resource - container (c) - blob (b)
  1120. * @param string $permissions Signed permissions - read (r), write (w), delete (d) and list (l)
  1121. * @param string $start The time at which the Shared Access Signature becomes valid.
  1122. * @param string $expiry The time at which the Shared Access Signature becomes invalid.
  1123. * @param string $identifier Signed identifier
  1124. * @return string
  1125. */
  1126. public function generateSharedAccessUrl($containerName = '', $blobName = '', $resource = 'b', $permissions = 'r', $start = '', $expiry = '', $identifier = '')
  1127. {
  1128. if ($containerName === '') {
  1129. throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
  1130. }
  1131. if (!self::isValidContainerName($containerName)) {
  1132. 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.');
  1133. }
  1134. // Resource name
  1135. $resourceName = self::createResourceName($containerName , $blobName);
  1136. // Generate URL
  1137. return $this->getBaseUrl() . '/' . $resourceName . '?' .
  1138. $this->_sharedAccessSignatureCredentials->createSignedQueryString(
  1139. $resourceName,
  1140. '',
  1141. $resource,
  1142. $permissions,
  1143. $start,
  1144. $expiry,
  1145. $identifier);
  1146. }
  1147. /**
  1148. * Register this object as stream wrapper client
  1149. *
  1150. * @param string $name Protocol name
  1151. * @return Zend_Service_WindowsAzure_Storage_Blob
  1152. */
  1153. public function registerAsClient($name)
  1154. {
  1155. self::$_wrapperClients[$name] = $this;
  1156. return $this;
  1157. }
  1158. /**
  1159. * Unregister this object as stream wrapper client
  1160. *
  1161. * @param string $name Protocol name
  1162. * @return Zend_Service_WindowsAzure_Storage_Blob
  1163. */
  1164. public function unregisterAsClient($name)
  1165. {
  1166. unset(self::$_wrapperClients[$name]);
  1167. return $this;
  1168. }
  1169. /**
  1170. * Get wrapper client for stream type
  1171. *
  1172. * @param string $name Protocol name
  1173. * @return Zend_Service_WindowsAzure_Storage_Blob
  1174. */
  1175. public static function getWrapperClient($name)
  1176. {
  1177. return self::$_wrapperClients[$name];
  1178. }
  1179. /**
  1180. * Register this object as stream wrapper
  1181. *
  1182. * @param string $name Protocol name
  1183. */
  1184. public function registerStreamWrapper($name = 'azure')
  1185. {
  1186. /**
  1187. * @see Zend_Service_WindowsAzure_Storage_Blob_Stream
  1188. */
  1189. require_once 'Zend/Service/WindowsAzure/Storage/Blob/Stream.php';
  1190. stream_register_wrapper($name, 'Zend_Service_WindowsAzure_Storage_Blob_Stream');
  1191. $this->registerAsClient($name);
  1192. }
  1193. /**
  1194. * Unregister this object as stream wrapper
  1195. *
  1196. * @param string $name Protocol name
  1197. * @return Zend_Service_WindowsAzure_Storage_Blob
  1198. */
  1199. public function unregisterStreamWrapper($name = 'azure')
  1200. {
  1201. stream_wrapper_unregister($name);
  1202. $this->unregisterAsClient($name);
  1203. }
  1204. /**
  1205. * Create resource name
  1206. *
  1207. * @param string $containerName Container name
  1208. * @param string $blobName Blob name
  1209. * @return string
  1210. */
  1211. public static function createResourceName($containerName = '', $blobName = '')
  1212. {
  1213. // Resource name
  1214. $resourceName = $containerName . '/' . $blobName;
  1215. if ($containerName === '' || $containerName === '$root') {
  1216. $resourceName = $blobName;
  1217. }
  1218. if ($blobName === '') {
  1219. $resourceName = $containerName;
  1220. }
  1221. return $resourceName;
  1222. }
  1223. /**
  1224. * Is valid container name?
  1225. *
  1226. * @param string $containerName Container name
  1227. * @return boolean
  1228. */
  1229. public static function isValidContainerName($containerName = '')
  1230. {
  1231. if ($containerName == '$root') {
  1232. return true;
  1233. }
  1234. if (!ereg("^[a-z0-9][a-z0-9-]*$", $containerName)) {
  1235. return false;
  1236. }
  1237. if (strpos($containerName, '--') !== false) {
  1238. return false;
  1239. }
  1240. if (strtolower($containerName) != $containerName) {
  1241. return false;
  1242. }
  1243. if (strlen($containerName) < 3 || strlen($containerName) > 63) {
  1244. return false;
  1245. }
  1246. if (substr($containerName, -1) == '-') {
  1247. return false;
  1248. }
  1249. return true;
  1250. }
  1251. /**
  1252. * Get error message from Zend_Http_Response
  1253. *
  1254. * @param Zend_Http_Response $response Repsonse
  1255. * @param string $alternativeError Alternative error message
  1256. * @return string
  1257. */
  1258. protected function _getErrorMessage(Zend_Http_Response $response, $alternativeError = 'Unknown error.')
  1259. {
  1260. $response = $this->_parseResponse($response);
  1261. if ($response && $response->Message) {
  1262. return (string)$response->Message;
  1263. } else {
  1264. return $alternativeError;
  1265. }
  1266. }
  1267. /**
  1268. * Generate block id
  1269. *
  1270. * @param int $part Block number
  1271. * @return string Windows Azure Blob Storage block number
  1272. */
  1273. protected function _generateBlockId($part = 0)
  1274. {
  1275. $returnValue = $part;
  1276. while (strlen($returnValue) < 64) {
  1277. $returnValue = '0' . $returnValue;
  1278. }
  1279. return $returnValue;
  1280. }
  1281. }