javascriptazureazure-blob-storagenext.js13azure-sdk-js

upload filesusing azure blob storage sdk


I am trying to upload files from Next js 13 application to azure blob storage

I think my file is an object and it expects a blob or buffer, I tried doing that but not of much help either.

my component code

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
"use client";

import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useToast } from "@/components/ui/use-toast";
import { DialogClose } from "@radix-ui/react-dialog";
import { useState } from "react";
import { ButtonLoading } from "../LoadingButton";
import axios from 'axios';


export default function UploadData() {
  const [file, setFile] = useState<File | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const { toast } = useToast();


  const handleUrlInput = (event: React.ChangeEvent<HTMLInputElement>) => {
    const selectedFile = event.target.files?.[0]
    if (selectedFile) {
      setFile(selectedFile);
    }
  };

  const handleSubmit = async () => {

      const formData = new FormData();
      if (file) {
        formData.append('file', file);
      }
      console.log(formData)


    const response = axios.post('/api/ingest', formData, {
      headers: {
        'Content-Type': 'multipart/form-data',
      },
    })

    // Handle the response
    if ((await response).status === 200) {
      // File uploaded successfully
      console.log('File uploaded successfully!');
    } else {
      // Something went wrong
      console.log('Error uploading file:', response);
    }
  }

  return (
    <Dialog>
      <DialogTrigger asChild>
        {isLoading ? <ButtonLoading /> : <Button>Upload</Button>}
      </DialogTrigger>
      <DialogContent className="sm:max-w-[425px]">
        <DialogHeader>
          <DialogTitle>Edit profile</DialogTitle>
          <DialogDescription>
            Enter URL which you want to provide as input for retrieving
            information.
          </DialogDescription>
        </DialogHeader>
        <div className="grid gap-4 py-4">
          <div className="grid grid-cols-4 items-center gap-4">
            <Label htmlFor="name">URL</Label>
            <Input
              id="name"
              type="file"
              onChange={handleUrlInput}
              className="col-span-full"
            />
          </div>
        </div>
        <DialogFooter>
          <DialogClose asChild>
            <Button
              type="submit"
              onClick={handleSubmit}
            >
              Submit
            </Button>
          </DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

My API code

export async function POST(request: Request, res: Response) {
  try {
    // const file = (await request.formData())?.get("file") as Blob;
    const formData = await request.formData();
    const filePart = formData.get("file");
    console.log("filePart", typeof filePart);

    if (filePart instanceof Blob) {
      const fileName = filePart.name;
      const buffer = Buffer.from(await filePart.arrayBuffer());
      console.log("File Name:", fileName);
      console.log("Buffer:", buffer);
      const blobClient = containerClient.getBlockBlobClient(fileName);
      await blobClient.uploadData(buffer);
      return NextResponse.json(res);
    } else {
      console.log("Invalid file format");
      return NextResponse.error();
    }
  } catch (e) {
    console.log(e);
    return NextResponse.error();
  }
}

I am not able to upload files, the error displayed is Invalid file format:

⨯ RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: 0

Solution

  • I am not able to upload files, the error displayed is:

    Invalid file format ⨯ RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: 0

    According to this SO-thread by Gaurav Mantri, Azure blob storage doesn't support the multipart/form-data content type.

    To upload multiple files you can use application/octet-stream or if you need to upload an image you can use the content-type= image/jpeg

    Here is a sample code that I tried to upload images to Azure blob storage using the next.js app.

    Code:

    azconnect.js

    import { BlobServiceClient } from '@azure/storage-blob';
    
    const connectionString = process.env.AZURE_CONNECTION;
    const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString);
    const containerName = process.env.AZURE_CONTAINER_NAME;
    
    async function uploadImageToAzureStorage(fileBuffer, fileName, contentType) {
      const containerClient = blobServiceClient.getContainerClient(containerName);
      const blockBlobClient = containerClient.getBlockBlobClient(fileName);
    
      try {
        await blockBlobClient.uploadData(
          fileBuffer,
          {
            blobHTTPHeaders: { blobContentType: contentType },
          }
        );
    
        return blockBlobClient.url;
      } catch (error) {
        console.error('Error uploading image:', error);
        throw error;
      }
    }
    
    export { uploadImageToAzureStorage }
    

    Components:

    import { useState } from 'react';
    
    const ImageUploadForm = () => {
      const [image, setImage] = useState(null);
    
      const handleSubmit = (e) => {
        const reader = new FileReader();
        reader.readAsDataURL(e.target.files[0]);
    
        reader.onload = () => {
          setImage(reader.result);
        };
        reader.onerror = () => {
          console.log('reader.error', reader.error);
        };
      };
    
      const uploadImage = async () => {
        try {
          const headers = {
            'Content-Type': 'image/jpeg', 
          };
    
          const response = await fetch('/api/postImage', {
            method: 'POST',
            body: image,
            headers: headers,
          });
    
          if (response.ok) {
            console.log('Image uploaded successfully!');
          } else {
            console.error('Image upload failed.');
          }
        } catch (error) {
          console.error('Error uploading image:', error);
        }
      };
    
      return (
        <>
          <input type="file" accept="image/*" onChange={handleSubmit} />
          <button onClick={uploadImage}>Upload Image</button>
        </>
      );
    };
    
    export default ImageUploadForm;
    

    Browser: enter image description here

    Portal: enter image description here

    Here is my GitHub link of the entire project to upload a sample image file to Azure blob storage using next.js.