Upload an image to an Azure Storage blob with JavaScript

Use a static web app to upload a file to an Azure Storage blob using an Azure Storage @azure/storage-blob npm package with an Azure Storage SAS token.

Prerequisites

Application architecture

This application architecture includes two Azure resources:

  • Azure Static Web Apps for the statically generated client application. The resource also provides the managed Azure Functions API. Managed means that the Static Web Apps resource manages the API resource for its own use.
  • Azure Storage for the blob storage.

Diagram showing how a customer interacts from their computer to use the website to upload a file to Azure Storage directly.

Step Description
1 The customer connects to the statically generated website. The website is hosted in Azure Static Web Apps.
2 The customer uses that website, to select a file to upload. For this tutorial, the front-end framework is Vite React and the file uploaded is an image file.
3 The website calls the Azure Functions API sas to get a SAS token based on the exact filename of the file to upload. The serverless API uses the Azure Blob Storage SDK to create the SAS token. The API returns the full URL to use to upload the file, which includes the SAS token as the query string.
https://YOUR-STORAGE-NAME.blob.core.windows.net/YOUR-CONTAINER/YOUR-FILE-NAME?YOUR-SAS-TOKEN
4 The front-end website uses the SAS token URL to upload the file directly to Azure Blob Storage.

Local and build environments

This tutorial uses the following environments:

  • Local development with GitHub Codespaces or Visual Studio Code.
  • Build and deploy with GitHub Actions.

1. Fork sample application repository with GitHub

This tutorial uses GitHub actions to deploy the sample application to Azure. You need a GitHub account and a fork of the sample application repository to complete that deployment.

  1. In a web browser, use the following link to begin the fork for your own account of the sample repository: Azure-Samples/azure-typescript-e2e-apps.
  2. Complete the steps to fork the sample with the main branch only.

2. Configure dev environment

A development container environment is available with all dependencies required to complete every exercise in this project. You can run the development container in GitHub Codespaces or locally using Visual Studio Code.

GitHub Codespaces runs a development container managed by GitHub with Visual Studio Code for the Web as the user interface. For the most straightforward development environment, use GitHub Codespaces so that you have the correct developer tools and dependencies preinstalled to complete this training module.

Important

All GitHub accounts can use Codespaces for up to 60 hours free each month with 2 core instances. For more information, see GitHub Codespaces monthly included storage and core hours.

  1. In a web browser, on your GitHub fork of the sample repository, start the process to create a new GitHub Codespace on the main branch of your fork by selecting the CODE button.

    GitHub screenshot of Code Spaces buttons for a repository.

  2. On the Codespaces tab, select the ellipsis, ....

    GitHub screenshot of Code Spaces tab with ellipsis control highlighted.

  3. Select + New with options to select a specific Codespaces dev container.

    GitHub screenshot of Codespaces New with options menu item highlighted.

  4. Select the following options then select Create codespace.

    • Branch: main
    • Dev container configuration: Tutorial: Upload file to storage with SAS Token
    • Region: accept default
    • Machine type: accept default

    GitHub screenshot of Codespaces New with options menu with the following dev container highlighted, Tutorial: Upload file to storage with SAS Token.

  5. Wait for the codespace to start. This startup process can take a few minutes.

  6. Open a new terminal in the codespace.

    Tip

    You can use the main menu to navigate to the Terminal menu option and then select the New Terminal option.

    Screenshot of the codespaces menu option to open a new terminal.

  7. Check the versions of the tools you use in this tutorial.

    node --version
    npm --version
    func --version
    

    This tutorial requires the following versions of each tool, which are preinstalled in your environment:

    Tool Version
    Node.js ≥ 18
    npm ≥ 9.5
    Azure Functions core tools ≥ 4.5098
  8. Close the terminal.

  9. The remaining steps in this tutorial take place in the context of this development container.

3. Install dependencies

The sample app for this tutorial is in the azure-upload-file-to-storage folder. You won't need to use any other folders in the project.

  1. In Visual Studio Code, open a terminal, and move to the project folder.

    cd azure-upload-file-to-storage
    
  2. Split the terminal so you have two terminals, one for the client app and one for the API app.

  3. In one of the terminals, run the following command to install the API app's dependencies and run the app.

    cd api && npm install
    
  4. In the other terminal, run the command to install the client app.

    cd app && npm install
    

4. Create Storage resource with Visual Studio extension

Create the Storage resource to use with the sample app. Storage is used for:

  • Triggers in the Azure Functions app
  • Blob (file) storage
  1. Navigate to the Azure Storage extension.

  2. Sign in to Azure if necessary.

  3. Right-click on the subscription then select Create Resource....

    Screenshot of Visual Studio Code in the Azure Explorer with the right-click menu showing the Create Resource item highlighted.

  4. Select Create Storage Account from list.

  5. Follow the prompts using the following table to understand how to create your Storage resource.

    Property Value
    Enter a globally unique name for the new web app. Enter a unique value such as fileuploadstor, for your Storage resource name.

    This unique name is your resource name used in the next section. Use only characters and numbers, up to 24 in length. You need this account name to use later.
    Select a location for new resources. Use the recommended location.
  6. When the app creation process is complete, a notification appears with information about the new resource.

    Screenshot of Visual Studio Code showing the Azure Activity Bar and the notification that the storage account was successfully created.

5. Configure Storage CORS

Because the browser is used to upload the file, the Azure Storage account needs to configure CORS to allow cross-origin requests.

  1. Navigate to the Azure Storage extension. Right-click on your storage resource and select Open in Portal.

  2. In the Azure portal storage account Settings section, select Resource sharing (CORS).

  3. Use the following properties to set CORS for this tutorial.

    • Allowed origins: *
    • Allowed methods: All except patch
    • Allowed headers: *
    • Exposed headers: *
    • Max age: 86400

    These settings are used for this tutorial to simplify the steps and aren't meant to indicate best practices or security. Learn more about CORS for Azure Storage.

  4. Select Save.

6. Grant anonymous access to storage

The file upload is secured from the client when you create a time-limited and permission-limited SAS token. However, once the file is uploaded, in this tutorial scenario, you want anyone to see it. In order to do that, you need to change the storage permission to be publicly accessible.

Even though the account is publicly accessible, each container and each blob can have private access. A more secure method but too complicated for this tutorial is to upload to one storage account with the SAS token, then move the blob to another storage account with public access.

  1. To enable public access in the Azure portal, select the Overview page for your storage account, in the Properties section, select Blob anonymous access then select Disabled.
  2. On the Configuration page, enable Allow Blob anonymous access.

7. Create upload container

  1. While still in the Azure portal storage account, in the Data storage section, select Containers.

  2. Select + Container to create your upload container with the following settings:

    • Name: upload
    • Public access Level: Blob
  3. Select Create.

8. Grant yourself Blob Data access

While you created the resource, you don't have permission to view the contents of the container. That is reserved for specific IAM roles. Add your account so you can view the blobs in the containers.

  1. Still in the Azure portal storage account, select Access Control (IAM).
  2. Select Add role assignments.
  3. Search and select Storage Blob Data Contributor. Select Next.
  4. Select + Select members.
  5. Search and select your account.
  6. Select Review + assign.
  7. Select Containers then the upload container. You should be able to see there are no blobs in the container without authorization errors.

9. Get Storage resource credentials

The Storage resource credentials are used in the Azure Functions API app to connect to the Storage resource.

  1. While still in the Azure portal, in the Security + networking section, select Access keys.

  2. Remember the API files are found at ./workspaces/azure-typescript-e2e-apps/azure-upload-file-to-storage/api.

  3. In the API folder, rename the file from local.settings.json.sample to local.settings.json. The file is ignored by Git so it won't be checked into source control.

  4. Update the settings for local.settings.json using the following table.

    Property Value Description
    Azure_Storage_AccountName Azure Storage account name, for example: fileuploadstor. Used in source code to connect to Storage resource.
    Azure_Storage_AccountKey Azure Storage account key Used in source code to connect to Storage resource.
    AzureWebJobsStorage Azure Storage account connection string Use by Azure Functions runtime to store state and logs.

It may seem like you entered the same account credentials twice, once as a key and once as a connection string. You did, but specifically for this simple tutorial. Generally speaking, Azure Functions apps should have a separate Storage resource that isn't reused for another purpose. When you create the Azure Function resource later in the tutorial, you won't need to set the AzureWebJobsStorage value for the cloud resource. You'll only need to set the Azure_Storage_AccountName and Azure_Storage_AccountKey values which are used in source code.

10. Run the API app

Run the Functions App to make sure it works correctly before deploying it to Azure.

  1. In the API app's terminal, run the following command to start the API app.

    npm run start
    
  2. Wait until the Azure Functions app is started. You'll get a notice that the Azure Functions app's port, 7071 is now available. You should also see the APIs listed in the terminal for the API app.

    Functions:
    
            list: [POST,GET] http://localhost:7071/api/list
    
            sas: [POST,GET] http://localhost:7071/api/sas
    
            status: [GET] http://localhost:7071/api/status
    
  3. Select the Ports tab in the bottom pane then right-click the 7071 port and select Port Visibility then select Public.

    If you don't expose this app as public, you'll get an error when you use the API from the client app.

  4. To test that the API works and connects to storage, in the Ports tab in the bottom pane, select the globe icon in the Local Address area for port 7071. This opens a web browser to the functions app.

  5. Add the API route to the URL address bar: /api/sas?container=upload&file=test.png. It's ok that the file isn't in the container yet. The API creates the SAS token based on where you want it to be uploaded to.

  6. The JSON response should look something like the following:

    {
        "url":"https://YOUR-STORAGE-RESOURCE.blob.core.windows.net/upload/test.png?sv=2023-01-03&spr=https&st=2023-07-26T22%3A15%3A59Z&se=2023-07-26T22%3A25%3A59Z&sr=b&sp=w&sig=j3Yc..."
    }
    
  7. Copy the base of the API URL in the browser address bar (not the SAS token URL in the JSON object) to use in the next step. The base URL is everything before /api/sas.

11. Configure and run the client app

  1. Rename the ./azure-upload-file-to-storage/app/.env.sample file to .env.

  2. Open the .env file and paste the base URL from the previous section as the value for the VITE_API_SERVER.

    An example for a Codespaces environment may look something like VITE_API_SERVER=https://improved-space-fishstick-pgvxvxjpqgrh6qxp-7071.app.github.dev

  3. In the other split terminal, start the client app with the following command:

    npm run dev
    
  4. Wait until the terminal returns the following notice that the app is available on port 5173.

      VITE v4.4.4  ready in 410 ms
    
      ➜  Local:   https://localhost:5173/
      ➜  Network: use --host to expose
      ➜  press h to show help
    
  5. Select the Ports tab in the bottom pane then right-click the 5173 port and select the globe icon.

  6. You should see the simple web app.

    Screenshot of web browser showing web app with Select File button available.

  7. Interact with the web app:

    • Select an image file (*.jpg or *.png) from your local computer to upload.
    • Select the Get a SAS button to request a SAS token from the API app. The response shows the full URL to use to upload the file to Storage.
    • Select the Upload button to send the image file directly to Storage.

    Screenshot of web browser showing web app with the image file uploaded and a thumbnail of the file displayed.

  8. The client app and the API app successfully worked together in a containerized developer environment.

12. Commit code changes

  1. In Visual Studio Code, open the Source Control tab.
  2. Select the + icon to stage all changes. These changes should only include new package-lock.json files for the app and api folders for this tutorial.

13. Deploy static web app to Azure

The Azure Functions app is using a preview feature, it must be deployed to West US 2 to function properly.

  1. In Visual Studio Code, select the Azure explorer.

  2. In the Azure Explorer, right-click on the subscription name then select Create Resource....

  3. Select Create Static Web App from list.

  4. Follow the prompts using the following table to understand how to create your Static Web App resource.

    Property Value
    Enter a globally unique name for the new web app. Enter a unique value such as fileuploadstor, for your Storage resource name.

    This unique name is your resource name used in the next section. Use only characters and numbers, up to 24 in length. You need this account name to use later.
    Select a location for new resources. Use the recommended location.
  5. Follow the prompts to provide the following information:

    Prompt Enter
    Select a resource group for new resources. Use the resource group that you created for your storage resource.
    Enter the name for the new static web app. Accept the default name.
    Select a SKU Select the free SKU for this tutorial. If you already have a free Static Web App resource in your subscription, select the next pricing tier.
    Choose build preset to configure default project structure. Select Custom.
    Select the location of your application code azure-upload-file-to-storage/app
    Select the location of your Azure Functions code azure-upload-file-to-storage/api
    Enter the path of your build output... dist

    This is the path from your app to your static (generated) files.
    Select a location for new resources. Select a region close to you.
  6. When the process is complete, a notification pop-up displays. Select View/Edit Workflow.

  7. Your remote fork has a new workflow file for deploying to Static Web Apps. Pull that file down to your environment with the following command in the terminal:

    git pull origin main
    
  8. Open the workflow file located at /.github/workflows/.

  9. Verify he section of the workflow specific to this tutorial's Static Web app should look like:

    ###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
    # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
    app_location: "/azure-upload-file-to-storage/app" # App source code path
    api_location: "/azure-upload-file-to-storage/api" # Api source code path - optional
    output_location: "dist" # Built app content directory - optional
    ###### End of Repository/Build Configurations ######
    
  10. Go to your GitHub fork of the sample, https://github.com/YOUR-ACCOUNT/azure-typescript-e2e-apps/actions to verify the build and deploy action, named Azure Static Web Apps CI/CD, completed successfully. This may take a few minutes to complete.

  11. Go to your Azure portal for your app and view the APIs section of Settings. The Backend Resource Name in the production environment is (managed) indicating your APIs have been successfully deployed.

  12. Select (managed) to see the list of APIs loaded in the app:

    • list
    • sas
    • status
  13. Go to the Overview page to find the URL for your deployed app.

  14. The deployment of the app is complete.

14. Configure API with Storage resource name and key

The app needs the Azure Storage resource name and key before the API works correctly.

  1. Still in the Azure Explorer, right-click on the Static Web App resource and select Open in Portal.

  2. Select Configuration in the Settings section.

  3. Add application settings using the following table.

    Property Value Description
    Azure_Storage_AccountName Azure Storage account name, for example: fileuploadstor. Used in source code to connect to Storage resource.
    Azure_Storage_AccountKey Azure Storage account key Used in source code to connect to Storage resource.
  4. Select Save on the Configuration page to save both settings.

Note

You don't need to set the client app's env variable VITE_API_SERVER because the client app and the API are hosted from the same domain.

15. Use the Azure-deployed static web app

Verify the deploy and configuration succeeded by using the web site.

  1. In Visual Studio Code, right-click your Static web app from the Azure explorer and select Browse site.
  2. In the new web browser window, select Choose File then select an image file (*.png or *.jpg) to upload.
  3. Select Get sas token. This action passes the file name to the API and receives the SAS token URL necessary to upload the file.
  4. Select Upload file to use the SAS token URL to upload the file. The browser displays the thumbnail and URL of the uploaded file.

16. Clean up resources

In Visual Studio Code, use the Azure explorer for Resource Groups, right-click on your resource group then select Delete.

This deletes all resources in the group, including your Storage and Static Web app resources.

Troubleshooting

Report issues with this sample in the GitHub repo noted below. Include the following with the issue:

  • The URL of the article
  • The step or context within the article that was problematic
  • Your development environment

Sample code

If you would like to continue with this app, learn how to deploy the app to Azure for hosting with one of the following choices: