|
1 <?php |
|
2 /** |
|
3 * LICENSE |
|
4 * |
|
5 * This source file is subject to the new BSD license that is bundled |
|
6 * with this package in the file LICENSE.txt. |
|
7 * It is also available through the world-wide-web at this URL: |
|
8 * http://framework.zend.com/license/new-bsd |
|
9 * If you did not receive a copy of the license and are unable to |
|
10 * obtain it through the world-wide-web, please send an email |
|
11 * to license@zend.com so we can send you a copy immediately. |
|
12 * |
|
13 * @category Zend |
|
14 * @package Zend_Cloud |
|
15 * @subpackage StorageService |
|
16 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
17 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
18 */ |
|
19 |
|
20 require_once 'Zend/Cloud/StorageService/Adapter.php'; |
|
21 require_once 'Zend/Service/WindowsAzure/Storage/Blob.php'; |
|
22 require_once 'Zend/Cloud/StorageService/Exception.php'; |
|
23 |
|
24 /** |
|
25 * |
|
26 * Windows Azure Blob Service abstraction |
|
27 * |
|
28 * @category Zend |
|
29 * @package Zend_Cloud |
|
30 * @subpackage StorageService |
|
31 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
32 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
33 */ |
|
34 class Zend_Cloud_StorageService_Adapter_WindowsAzure |
|
35 implements Zend_Cloud_StorageService_Adapter |
|
36 { |
|
37 const ACCOUNT_NAME = 'storage_accountname'; |
|
38 const ACCOUNT_KEY = 'storage_accountkey'; |
|
39 const HOST = "storage_host"; |
|
40 const PROXY_HOST = "storage_proxy_host"; |
|
41 const PROXY_PORT = "storage_proxy_port"; |
|
42 const PROXY_CREDENTIALS = "storage_proxy_credentials"; |
|
43 const CONTAINER = "storage_container"; |
|
44 const RETURN_TYPE = 'return_type'; |
|
45 const RETURN_PATHNAME = 'return_path'; |
|
46 const RETURN_OPENMODE = 'return_openmode'; |
|
47 |
|
48 /** return types for fetch */ |
|
49 const RETURN_PATH = 1; // return filename |
|
50 const RETURN_STRING = 2; // return data as string |
|
51 const RETURN_STREAM = 3; // return PHP stream |
|
52 |
|
53 /** return types for list */ |
|
54 const RETURN_LIST = 1; // return native list |
|
55 const RETURN_NAMES = 2; // return only names |
|
56 |
|
57 const DEFAULT_HOST = Zend_Service_WindowsAzure_Storage::URL_CLOUD_BLOB; |
|
58 |
|
59 /** |
|
60 * Storage container to operate on |
|
61 * |
|
62 * @var string |
|
63 */ |
|
64 protected $_container; |
|
65 |
|
66 /** |
|
67 * Storage client |
|
68 * |
|
69 * @var Zend_Service_WindowsAzure_Storage_Blob |
|
70 */ |
|
71 protected $_storageClient = null; |
|
72 |
|
73 /** |
|
74 * Creates a new Zend_Cloud_Storage_WindowsAzure instance |
|
75 * |
|
76 * @param array|Zend_Config $options Options for the Zend_Cloud_Storage_WindowsAzure instance |
|
77 */ |
|
78 public function __construct($options = array()) |
|
79 { |
|
80 if ($options instanceof Zend_Config) { |
|
81 $options = $options->toArray(); |
|
82 } |
|
83 |
|
84 if (!is_array($options)) { |
|
85 throw new Zend_Cloud_StorageService_Exception('Invalid options provided'); |
|
86 } |
|
87 |
|
88 // Build Zend_Service_WindowsAzure_Storage_Blob instance |
|
89 if (!isset($options[self::HOST])) { |
|
90 $host = self::DEFAULT_HOST; |
|
91 } else { |
|
92 $host = $options[self::HOST]; |
|
93 } |
|
94 |
|
95 if (!isset($options[self::ACCOUNT_NAME])) { |
|
96 throw new Zend_Cloud_StorageService_Exception('No Windows Azure account name provided.'); |
|
97 } |
|
98 if (!isset($options[self::ACCOUNT_KEY])) { |
|
99 throw new Zend_Cloud_StorageService_Exception('No Windows Azure account key provided.'); |
|
100 } |
|
101 |
|
102 $this->_storageClient = new Zend_Service_WindowsAzure_Storage_Blob($host, |
|
103 $options[self::ACCOUNT_NAME], $options[self::ACCOUNT_KEY]); |
|
104 |
|
105 // Parse other options |
|
106 if (!empty($options[self::PROXY_HOST])) { |
|
107 $proxyHost = $options[self::PROXY_HOST]; |
|
108 $proxyPort = isset($options[self::PROXY_PORT]) ? $options[self::PROXY_PORT] : 8080; |
|
109 $proxyCredentials = isset($options[self::PROXY_CREDENTIALS]) ? $options[self::PROXY_CREDENTIALS] : ''; |
|
110 |
|
111 $this->_storageClient->setProxy(true, $proxyHost, $proxyPort, $proxyCredentials); |
|
112 } |
|
113 |
|
114 if (isset($options[self::HTTP_ADAPTER])) { |
|
115 $this->_storageClient->setHttpClientChannel($options[self::HTTP_ADAPTER]); |
|
116 } |
|
117 |
|
118 // Set container |
|
119 $this->_container = $options[self::CONTAINER]; |
|
120 |
|
121 // Make sure the container exists |
|
122 if (!$this->_storageClient->containerExists($this->_container)) { |
|
123 $this->_storageClient->createContainer($this->_container); |
|
124 } |
|
125 } |
|
126 |
|
127 /** |
|
128 * Get an item from the storage service. |
|
129 * |
|
130 * @param string $path |
|
131 * @param array $options |
|
132 * @return mixed |
|
133 */ |
|
134 public function fetchItem($path, $options = null) |
|
135 { |
|
136 // Options |
|
137 $returnType = self::RETURN_STRING; |
|
138 $returnPath = tempnam('', 'azr'); |
|
139 $openMode = 'r'; |
|
140 |
|
141 // Parse options |
|
142 if (is_array($options)) { |
|
143 if (isset($options[self::RETURN_TYPE])) { |
|
144 $returnType = $options[self::RETURN_TYPE]; |
|
145 } |
|
146 |
|
147 if (isset($options[self::RETURN_PATHNAME])) { |
|
148 $returnPath = $options[self::RETURN_PATHNAME]; |
|
149 } |
|
150 |
|
151 if (isset($options[self::RETURN_OPENMODE])) { |
|
152 $openMode = $options[self::RETURN_OPENMODE]; |
|
153 } |
|
154 } |
|
155 |
|
156 // Fetch the blob |
|
157 try { |
|
158 $this->_storageClient->getBlob( |
|
159 $this->_container, |
|
160 $path, |
|
161 $returnPath |
|
162 ); |
|
163 } catch (Zend_Service_WindowsAzure_Exception $e) { |
|
164 if (strpos($e->getMessage(), "does not exist") !== false) { |
|
165 return false; |
|
166 } |
|
167 throw new Zend_Cloud_StorageService_Exception('Error on fetch: '.$e->getMessage(), $e->getCode(), $e); |
|
168 } |
|
169 |
|
170 // Return value |
|
171 if ($returnType == self::RETURN_PATH) { |
|
172 return $returnPath; |
|
173 } |
|
174 if ($returnType == self::RETURN_STRING) { |
|
175 return file_get_contents($returnPath); |
|
176 } |
|
177 if ($returnType == self::RETURN_STREAM) { |
|
178 return fopen($returnPath, $openMode); |
|
179 } |
|
180 } |
|
181 |
|
182 /** |
|
183 * Store an item in the storage service. |
|
184 * WARNING: This operation overwrites any item that is located at |
|
185 * $destinationPath. |
|
186 * @param string $destinationPath |
|
187 * @param mixed $data |
|
188 * @param array $options |
|
189 * @return boolean |
|
190 */ |
|
191 public function storeItem($destinationPath, $data, $options = null) |
|
192 { |
|
193 // Create a temporary file that will be uploaded |
|
194 $temporaryFilePath = ''; |
|
195 $removeTemporaryFilePath = false; |
|
196 |
|
197 if (is_resource($data)) { |
|
198 $temporaryFilePath = tempnam('', 'azr'); |
|
199 $fpDestination = fopen($temporaryFilePath, 'w'); |
|
200 |
|
201 $fpSource = $data; |
|
202 rewind($fpSource); |
|
203 while (!feof($fpSource)) { |
|
204 fwrite($fpDestination, fread($fpSource, 8192)); |
|
205 } |
|
206 |
|
207 fclose($fpDestination); |
|
208 |
|
209 $removeTemporaryFilePath = true; |
|
210 } elseif (file_exists($data)) { |
|
211 $temporaryFilePath = $data; |
|
212 $removeTemporaryFilePath = false; |
|
213 } else { |
|
214 $temporaryFilePath = tempnam('', 'azr'); |
|
215 file_put_contents($temporaryFilePath, $data); |
|
216 $removeTemporaryFilePath = true; |
|
217 } |
|
218 |
|
219 try { |
|
220 // Upload data |
|
221 $this->_storageClient->putBlob( |
|
222 $this->_container, |
|
223 $destinationPath, |
|
224 $temporaryFilePath |
|
225 ); |
|
226 } catch(Zend_Service_WindowsAzure_Exception $e) { |
|
227 @unlink($temporaryFilePath); |
|
228 throw new Zend_Cloud_StorageService_Exception('Error on store: '.$e->getMessage(), $e->getCode(), $e); |
|
229 } |
|
230 if ($removeTemporaryFilePath) { |
|
231 @unlink($temporaryFilePath); |
|
232 } |
|
233 } |
|
234 |
|
235 /** |
|
236 * Delete an item in the storage service. |
|
237 * |
|
238 * @param string $path |
|
239 * @param array $options |
|
240 * @return void |
|
241 */ |
|
242 public function deleteItem($path, $options = null) |
|
243 { |
|
244 try { |
|
245 $this->_storageClient->deleteBlob( |
|
246 $this->_container, |
|
247 $path |
|
248 ); |
|
249 } catch (Zend_Service_WindowsAzure_Exception $e) { |
|
250 throw new Zend_Cloud_StorageService_Exception('Error on delete: '.$e->getMessage(), $e->getCode(), $e); |
|
251 } |
|
252 } |
|
253 |
|
254 /** |
|
255 * Copy an item in the storage service to a given path. |
|
256 * |
|
257 * @param string $sourcePath |
|
258 * @param string $destinationPath |
|
259 * @param array $options |
|
260 * @return void |
|
261 */ |
|
262 public function copyItem($sourcePath, $destinationPath, $options = null) |
|
263 { |
|
264 try { |
|
265 $this->_storageClient->copyBlob( |
|
266 $this->_container, |
|
267 $sourcePath, |
|
268 $this->_container, |
|
269 $destinationPath |
|
270 ); |
|
271 } catch (Zend_Service_WindowsAzure_Exception $e) { |
|
272 throw new Zend_Cloud_StorageService_Exception('Error on copy: '.$e->getMessage(), $e->getCode(), $e); |
|
273 } |
|
274 } |
|
275 |
|
276 /** |
|
277 * Move an item in the storage service to a given path. |
|
278 * |
|
279 * @param string $sourcePath |
|
280 * @param string $destinationPath |
|
281 * @param array $options |
|
282 * @return void |
|
283 */ |
|
284 public function moveItem($sourcePath, $destinationPath, $options = null) |
|
285 { |
|
286 try { |
|
287 $this->_storageClient->copyBlob( |
|
288 $this->_container, |
|
289 $sourcePath, |
|
290 $this->_container, |
|
291 $destinationPath |
|
292 ); |
|
293 |
|
294 $this->_storageClient->deleteBlob( |
|
295 $this->_container, |
|
296 $sourcePath |
|
297 ); |
|
298 } catch (Zend_Service_WindowsAzure_Exception $e) { |
|
299 throw new Zend_Cloud_StorageService_Exception('Error on move: '.$e->getMessage(), $e->getCode(), $e); |
|
300 } |
|
301 |
|
302 } |
|
303 |
|
304 /** |
|
305 * Rename an item in the storage service to a given name. |
|
306 * |
|
307 * |
|
308 * @param string $path |
|
309 * @param string $name |
|
310 * @param array $options |
|
311 * @return void |
|
312 */ |
|
313 public function renameItem($path, $name, $options = null) |
|
314 { |
|
315 return $this->moveItem($path, $name, $options); |
|
316 } |
|
317 |
|
318 /** |
|
319 * List items in the given directory in the storage service |
|
320 * |
|
321 * The $path must be a directory |
|
322 * |
|
323 * |
|
324 * @param string $path Must be a directory |
|
325 * @param array $options |
|
326 * @return array A list of item names |
|
327 */ |
|
328 public function listItems($path, $options = null) |
|
329 { |
|
330 // Options |
|
331 $returnType = self::RETURN_NAMES; // 1: return list of paths, 2: return raw output from underlying provider |
|
332 |
|
333 // Parse options |
|
334 if (is_array($options)&& isset($options[self::RETURN_TYPE])) { |
|
335 $returnType = $options[self::RETURN_TYPE]; |
|
336 } |
|
337 |
|
338 try { |
|
339 // Fetch list |
|
340 $blobList = $this->_storageClient->listBlobs( |
|
341 $this->_container, |
|
342 $path |
|
343 ); |
|
344 } catch (Zend_Service_WindowsAzure_Exception $e) { |
|
345 throw new Zend_Cloud_StorageService_Exception('Error on list: '.$e->getMessage(), $e->getCode(), $e); |
|
346 } |
|
347 |
|
348 // Return |
|
349 if ($returnType == self::RETURN_LIST) { |
|
350 return $blobList; |
|
351 } |
|
352 |
|
353 $returnValue = array(); |
|
354 foreach ($blobList as $blob) { |
|
355 $returnValue[] = $blob->Name; |
|
356 } |
|
357 |
|
358 return $returnValue; |
|
359 } |
|
360 |
|
361 /** |
|
362 * Get a key/value array of metadata for the given path. |
|
363 * |
|
364 * @param string $path |
|
365 * @param array $options |
|
366 * @return array |
|
367 */ |
|
368 public function fetchMetadata($path, $options = null) |
|
369 { |
|
370 try { |
|
371 return $this->_storageClient->getBlobMetaData( |
|
372 $this->_container, |
|
373 $path |
|
374 ); |
|
375 } catch (Zend_Service_WindowsAzure_Exception $e) { |
|
376 if (strpos($e->getMessage(), "could not be accessed") !== false) { |
|
377 return false; |
|
378 } |
|
379 throw new Zend_Cloud_StorageService_Exception('Error on fetch: '.$e->getMessage(), $e->getCode(), $e); |
|
380 } |
|
381 } |
|
382 |
|
383 /** |
|
384 * Store a key/value array of metadata at the given path. |
|
385 * WARNING: This operation overwrites any metadata that is located at |
|
386 * $destinationPath. |
|
387 * |
|
388 * @param string $destinationPath |
|
389 * @param array $options |
|
390 * @return void |
|
391 */ |
|
392 public function storeMetadata($destinationPath, $metadata, $options = null) |
|
393 { |
|
394 try { |
|
395 $this->_storageClient->setBlobMetadata($this->_container, $destinationPath, $metadata); |
|
396 } catch (Zend_Service_WindowsAzure_Exception $e) { |
|
397 if (strpos($e->getMessage(), "could not be accessed") === false) { |
|
398 throw new Zend_Cloud_StorageService_Exception('Error on store metadata: '.$e->getMessage(), $e->getCode(), $e); |
|
399 } |
|
400 } |
|
401 } |
|
402 |
|
403 /** |
|
404 * Delete a key/value array of metadata at the given path. |
|
405 * |
|
406 * @param string $path |
|
407 * @param array $options |
|
408 * @return void |
|
409 */ |
|
410 public function deleteMetadata($path, $options = null) |
|
411 { |
|
412 try { |
|
413 $this->_storageClient->setBlobMetadata($this->_container, $destinationPath, array()); |
|
414 } catch (Zend_Service_WindowsAzure_Exception $e) { |
|
415 if (strpos($e->getMessage(), "could not be accessed") === false) { |
|
416 throw new Zend_Cloud_StorageService_Exception('Error on delete metadata: '.$e->getMessage(), $e->getCode(), $e); |
|
417 } |
|
418 } |
|
419 } |
|
420 |
|
421 /** |
|
422 * Delete container |
|
423 * |
|
424 * @return void |
|
425 */ |
|
426 public function deleteContainer() |
|
427 { |
|
428 try { |
|
429 $this->_storageClient->deleteContainer($this->_container); |
|
430 } catch (Zend_Service_WindowsAzure_Exception $e) { |
|
431 throw new Zend_Cloud_StorageService_Exception('Error on delete: '.$e->getMessage(), $e->getCode(), $e); |
|
432 } |
|
433 } |
|
434 |
|
435 /** |
|
436 * Get the concrete adapter. |
|
437 * @return Zend_Service_Azure_Storage_Blob |
|
438 */ |
|
439 public function getClient() |
|
440 { |
|
441 return $this->_storageClient; |
|
442 } |
|
443 } |