File upload in Cloud Applications: The Options

Almost every web application requires some form of file upload.  You may want to allow a user to upload a profile picture or to import any kind of data.

Multiple ways to implement the file upload

Depending on the size of the files and the regularity of the upload you have two options to implement the upload:

Directly upload the file to a data store

The fastest and resource friendliest way is to directly upload the file from the client to a data store. This typically requires the client to have the security credentials for the data store:

direct_uploadBut giving security credentials to potential untrusted clients isn’t a realistic approach for most web applications. Instead you want to use a token that provides clients restricted access to a specific resource for a limited validity period. This pattern is known as Valet Key pattern.

Upload the file through a middleware

The second option is to upload the file to your middleware (API) which will handle the movement of the data to the data store.

middleware_upload

This approach prevents us from exposing any information about the underlying data store to the client. We could even change our Data Store (e. g. from Azure Blob Storage to Azure File Storage) without updating the client.

The downside is that it absorbs valuable resources from our middleware like compute, memory and bandwidth.

Angular 6 application hosted on Azure Storage Static website

A few days ago Microsoft announced a new public preview feature for Azure Storage called Static website. It enables you to host a static web app using Azure Storage which is multiple times cheaper then a traditional required Web App. Reason enough to give it a try.

Create a Storage account

To use the Static website feature we need a general purpose V2 Storage Account. You can either manually create the resource within the Azure Portal or by using the following ARM Template:

Enable Static website feature

Unfortunately we can’t enable the Static website feature using an ARM Template (thanks to Gaurav Mantri) because it is not part of the Storage Resource Provider API. So we have to manually enable it after we’ve provisioned the Storage Account.

We can do that within our previous created Storage Account: Select Static website (preview) to configure a container for static website hosting:

enablestaticwebsite

Since our example applicaton will have an index.html,  we can leave the index document name field as it is.

Note: If you have to automate this process, it is still possible to enable this feature using the Azure Storage Services REST API.

After we save our changes we get the primary endpoint URL of our site:

spaurl.png

Create an Angular Single Page Application

We use the Angular CLI to scaffold an Angular sample applicaton that already has routing enabled. The name of our app will be spastore:

ng new spastore --routing

We also create a new component named subcomponent to demonstrate the routing capability:

ng g component subcomponent

And configure the routing module accordingly:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { SubcomponentComponent } from './subcomponent/subcomponent.component';

const routes: Routes = [
  { path: 'subcomponent', component: SubcomponentComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes, { useHash: true })],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Note that we use the HashLocationStrategy oppsed to the default PathLocationStrategy because the later will result in 404 errors. This is because we can’t define a rewrite URL for our static website and Azure will try to resolve the specified resource – e. g.:
https://spastore.z6.web.core.windows.net/subcomponent

The HashLocationStrategy represents its state in the hash fragment of the browser’s URL. So this will be the new route for our subcomponent:
https://spastore.z6.web.core.windows.net/#/subcomponent

Finally we add the router outlet to the app.component.html which displays our component and add a router link:


  <a>Subcomponent</a>

Now it’s time to compile the application and get a production build. Once again we use the Angular CLI:

ng build --prod

The build artefacts are stored within the dist folder and are ready to publish. We can do that by clicking on the $web link within the Static website (preview) blade. Then we have to click on the Upload button and select all files within the artefact (dist) folder:

angularupload

If we browse to the URL we can see our Angular App up and running and also the routing is working as expected:

angulardemo.png

You can test the site here: https://spastore.z6.web.core.windows.net

The source code including the ARM Template and the Angular sample application can be found in my GitHub repository.

Update:

You can actualy use the PathLocationStrategy by setting the error page to index.html (thanks to @nthonyChu). The first try to serve a URL like https://spastore.z6.web.core.windows.net/subcomponent will fail since it is not a valid resource. Then the path will get passed to the error page which is our index.html (page returns content with 404 status code). However, this is hack. I still recommend using the HashLocationStrategy until a rewrite mechanism is in place.

Rename Azure Storage Blob using PowerShell

At the time of writing this there is no API to rename an Azure Storage blob in one operation. You have to copy the blob and delete the original one after the copy process completes.

You can vote for the feature here: Rename blobs without needing to copy them

Until then you can use my convenience Rename-AzureStorageBlob cmdlet:


function Rename-AzureStorageBlob
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)]
        [Microsoft.WindowsAzure.Commands.Common.Storage.ResourceModel.AzureStorageBlob]$Blob,

        [Parameter(Mandatory=$true, Position=1)]
        [string]$NewName
    )

  Process {
    $blobCopyAction = Start-AzureStorageBlobCopy `
        -ICloudBlob $Blob.ICloudBlob `
        -DestBlob $NewName `
        -Context $Blob.Context `
        -DestContainer $Blob.ICloudBlob.Container.Name

    $status = $blobCopyAction | Get-AzureStorageBlobCopyState 

    while ($status.Status -ne 'Success')
    {
        $status = $blobCopyAction | Get-AzureStorageBlobCopyState
        Start-Sleep -Milliseconds 50
    }

    $Blob | Remove-AzureStorageBlob -Force
  }
}

It accepts the blob as pipeline input so you can pipe the result of the Get-AzureStorageBlob to it and just provide a new name:

$connectionString= 'DefaultEndpointsProtocol=https;AccountName....'
$storageContext = New-AzureStorageContext -ConnectionString $connectionString

Get-AzureStorageBlob -Container 'MyContainer' -Context $storageContext -Blob 'myBlob.txt'|
    Rename-AzureStorageBlob -NewName 'MyNewBlob.txt'

You can also download the script from my GitHub repository.