
I had the wonderful task to change a 20 year old PHP software during a Lift-and-Shift migration to Azure, so that it doesn’t store files directly in the web folder anymore, but uploads them to an Azure Blob Storage.
Dependencies
Microsoft provides a PHP SDK, with which the whole task can be done with a few lines of code. Only an additional component for identifying the mime type by the file extension is necessary.
You can use the PHP Composer to reference those dependencies
All settings are provided as environment vars.
- STORAGE_ACCOUNT_NAME: your Azure Storage account name
- STORAGE_ACCOUNT_KEY: your Azure Storage account key (secret)
The target storage container will be passed to the functions.
1<?php
2
3# reference autoload
4require_once(realpath($_SERVER["DOCUMENT_ROOT"]) . '/vendor/autoload.php');
5
6# imports
7use MicrosoftAzure\Storage\Blob\Models\CreateBlockBlobOptions;
8use MicrosoftAzure\Storage\Blob\BlobRestProxy;
9
10## adds file to the storage. Usage: storageAddFile("myContainer", "C:\path\to\file.png", "filename-on-storage.png")
11function storageAddFile($containerName, $file, $fileName)
12{
13 # Setup a specific instance of an Azure::Storage::Client
14 $connectionString = "DefaultEndpointsProtocol=https;AccountName=".getenv('STORAGE_ACCOUNT_NAME').";AccountKey=".getenv('STORAGE_ACCOUNT_KEY');
15 // Create blob client.
16 $blobClient = BlobRestProxy::createBlobService($connectionString);
17
18 $handle = @fopen($file, "r");
19 if($handle)
20 {
21 $options = new CreateBlockBlobOptions();
22 $mime = NULL;
23
24 try
25 {
26 // identify mime type
27 $mimes = new \Mimey\MimeTypes;
28 $mime = $mimes->getMimeType(pathinfo($fileName, PATHINFO_EXTENSION));
29 // set content type
30 $options->setContentType($mime);
31 }
32 catch ( Exception $e )
33 {
34 error_log("Failed to read mime from '".$file.": ". $e);
35 }
36
37 try
38 {
39 if($mime)
40 {
41 $cacheTime = getCacheTimeByMimeType($mime);
42 if($cacheTime)
43 {
44 $options->setCacheControl("public, max-age=".$cacheTime);
45 }
46 }
47
48 $blobClient->createBlockBlob($containerName, $fileName, $handle, $options);
49 } catch ( Exception $e ) {
50 error_log("Failed to upload file '".$file."' to storage: ". $e);
51 }
52
53 @fclose($handle);
54 return true;
55 } else {
56 error_log("Failed to open file '".$file."' to upload to storage.");
57 return false;
58 }
59}
60
61## get cache time by mime type
62function getCacheTimeByMimeType($mime)
63{
64 $mime = strtolower($mime);
65
66 $types = array(
67 "application/json" => 604800,// 7 days
68 "application/javascript" => 604800,// 7 days
69 "application/xml" => 604800,// 7 days
70 "application/xhtml+xml" => 604800,// 7 days
71 "image/bmp" => 604800,// 7 days
72 "image/gif" => 604800,// 7 days
73 "image/jpeg" => 604800,// 7 days
74 "image/png" => 604800,// 7 days
75 "image/tiff" => 604800,// 7 days
76 "image/svg+xml" => 604800,// 7 days
77 "image/x-icon" => 604800,// 7 days
78 "text/plain" => 604800, // 7 days
79 "text/html" => 604800,// 7 days
80 "text/css" => 604800,// 7 days
81 "text/richtext" => 604800,// 7 days
82 "text/xml" => 604800,// 7 days
83 );
84
85 // return value
86 if(array_key_exists($mime, $types))
87 {
88 return $types[$mime];
89 }
90
91 return FALSE;
92}
93
94## removes file from the storage. Usage: storageAddFile("myContainer", "filename-on-storage.png")
95function storageRemoveFile($containerName, $fileName)
96{
97 # Setup a specific instance of an Azure::Storage::Client
98 $connectionString = "DefaultEndpointsProtocol=https;AccountName=".getenv('STORAGE_ACCOUNT_NAME').";AccountKey=".getenv('STORAGE_ACCOUNT_KEY');
99 // Create blob client.
100 $blobClient = BlobRestProxy::createBlobService($connectionString);
101
102 try
103 {
104 $blobClient->deleteBlob($containerName, $fileName);
105 } catch ( Exception $e ) {
106 error_log("Failed to delete file '".$fileName."' from storage");
107 }
108
109 return true;
110}
111?>
The source code does not win a beauty award, but unfortunately cannot be integrated into the existing solution in any other way. Nevertheless, this snippet shows how easy it is to put files into the Azure Storage - even with 20 years old legacy code.
This temporary and will soon be completely replaced by an ASP.NET Core solution.
Related articles

Aug 17, 2026 - 17 min read
Automatic TTL in Azure Cosmos DB with .NET
Temporary data is easy to create and surprisingly difficult to remove reliably. Sessions, idempotency keys, import buffers, transient …

Jul 19, 2026 - 8 min read
Run Hono on Azure Static Web Apps: managed Functions APIs for static frontends
Azure Static Web Apps is primarily a host for static frontend assets, but it can also deploy a small HTTP API alongside the frontend. That …

Jul 12, 2026 - 14 min read
Run Hono on Azure: Container Apps, Functions, App Service and AKS compared
Hono is often associated with Cloudflare Workers and for good reason. Its small routing model, Web Standard API surface and direct fetch …
Let's Work Together
Looking for an experienced Platform Architect or Engineer for your next project? Whether it's cloud migration, platform modernization or building new solutions from scratch - I'm here to help you succeed.
