azurenext.jsazure-blob-storageblobimage-upload

How do I upload an image to Azure Blob Storage with NextJS API?


I have a React component that sends user's profile image to a proxy nextjs api (want to keep the proxy API so Azure secrets aren't visible in browser network tab).

Here's the front-end react code:

 const [image, setImage] = useState({ preview: '', raw: '' })

  const handleImageChange = (e) => {
    setImage({
      preview: URL.createObjectURL(e.target.files[0]),
      raw: e.target.files[0]
    })
  }

     // Form the request for sending picture to server.
     const picFile = new FormData()
     picFile.append("image", image.raw)
     const picOptions = {
      method: 'POST',
      headers: {
        'Content-type': 'multipart/form-data'
      },
      body: picFile,
    }

const picResponse = await fetch(profilePicEndpoint, picOptions)
return (
<input accept=".png, .jpg, .jpeg" type="file" id="image-upload" style={{ display: 'none' }} onChange={handleImageChange} />
)

And here's what the proxy (NextJS) api send to Azure Blob Storage via the SAS token

export default async function handler(req, res) {
    const body = req.body
    //handle sending profile pic to azure blob storage
    const blobEndpoint = 'https://STORAGEACCOUNT.blob.core.windows.net/MyContainerhere/Testfile.png?BunchofCredentialsHere'
    const blobOptions = {
      method: 'PUT',
      headers: {
        'x-ms-blob-type': 'BlockBlob'
      },
      body: req.body,
    }
    const blobResponse = await fetch(blobEndpoint, blobOptions)
    // console.log(blobResponse)

    // Sends a HTTP success code
    res.status(200).json({ message: ` Submission successful. Please give 24 hours for updates to be reflected on this page.` })
  }

It ends up transforming the png to a bunch of random characters (I'm assuming this is binary code of the file?) to the nextjs api, which ends up sending the same thing to the azure blob storage. I just want the file to upload to the nextjs api, then to azure blob storage as a png/jpeg/whatever image type.

Thanks for any help/ideas.


Solution

  • I tried below next.js code to upload an image as a blob to my storage account.

    Code:

    upload.js:

    import { BlobServiceClient } from '@azure/storage-blob';
    
    export default async function handler(req, res) {
      if (req.method === 'POST') {
        try {
          const { base64Image } = req.body;
    
          const storageAccount = '<account-name>';
          const containerName = 'container-name>';
          const accessKey = '<access-key>';
          const connectionString = `<connect-string>`;
    
          const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString);
          const containerClient = blobServiceClient.getContainerClient(containerName);
          const filename = `${Date.now()}.png`;
          const imageBuffer = Buffer.from(base64Image, 'base64');
          const blockBlobClient = containerClient.getBlockBlobClient(filename);
          await blockBlobClient.uploadData(imageBuffer, { blobHTTPHeaders: { blobContentType: 'image/png' } });
    
          res.status(200).json({ message: 'Image uploaded successfully' });
        } catch (error) {
          res.status(500).json({ error: 'Error occured' });
        }
      } else {
        res.status(405).json({ error: 'not allowed' });
      }
    }
    

    ImageUploader.js:

    import { useState } from 'react';
    import axios from 'axios';
    
    export default function ImageUploader() {
      const [selectedImage, setSelectedImage] = useState(null);
    
      const handleImageUpload = async () => {
        if (selectedImage) {
          try {
            const reader = new FileReader();
            reader.readAsDataURL(selectedImage);
            reader.onloadend = async () => {
              const base64Image = reader.result.split(',')[1];
    
              await axios.post('/api/upload', { base64Image });
            };
          } catch (error) {
          }
        }
      };
    
      const handleImageChange = (event) => {
        const file = event.target.files[0];
        setSelectedImage(file);
      };
    
      return (
        <div>
          <input type="file" accept="image/*" onChange={handleImageChange} />
          <button onClick={handleImageUpload}>Upload Image to storage account</button>
        </div>
      );
    }
    

    index.js:

    import ImageUploader from '../components/ImageUploader';  
      
    export default function Home() {  
    return (  
    <div>   
    <h1>Image Upload using Next-js</h1>  
    <ImageUploader />  
    </div>  
    );  
    }
    

    My project structure looks like below,

    enter image description here

    Output:

    It runs successfully as below,

    enter image description here

    With the above URL, I got the output in the browser as below,

    enter image description here

    choose an image file and click on upload image button like below,

    enter image description here

    Then, the image uploaded successfully to the storage account as below,

    enter image description here