You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 13应用上传Azure Blob Storage失败及错误排查

在Next.js 13中上传文件到Azure Blob Storage的问题

我正尝试在Next.js 13应用中上传文件至Azure Blob Storage,认为文件对象不符合Blob或Buffer要求,尝试转换后仍未解决问题。当前无法完成文件上传,报错信息如下:

Invalid file format:

⨯ RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: 0

前端组件代码

"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>
  );
}

API路由代码

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();
  }
}

问题修复方案

1. 修复API路由的响应错误

  • Next.js 13 App Router的API路由无需res参数,NextResponse.json(res)是错误用法,需返回自定义响应体与合法状态码
  • NextResponse.error()会默认返回状态码0(无效值),需手动指定错误状态码

修改后的API路由:

import { NextResponse } from 'next/server';
// 确保containerClient已正确初始化并引入

export async function POST(request: Request) {
  try {
    const formData = await request.formData();
    const filePart = formData.get("file");

    if (filePart instanceof Blob) {
      // 兼容非File类型的Blob,设置默认文件名
      const fileName = filePart instanceof File ? filePart.name : "unknown-file";
      const buffer = Buffer.from(await filePart.arrayBuffer());
      const blobClient = containerClient.getBlockBlobClient(fileName);
      await blobClient.uploadData(buffer);
      return NextResponse.json({ success: true, message: "文件上传成功" }, { status: 200 });
    } else {
      console.log("Invalid file format");
      return NextResponse.json({ error: "无效的文件格式" }, { status: 400 });
    }
  } catch (e) {
    console.error("上传失败:", e);
    return NextResponse.json({ error: "文件上传失败" }, { status: 500 });
  }
}

2. 优化前端请求逻辑

  • 手动设置Content-Type: multipart/form-data会丢失boundary信息,导致后端无法解析FormData,需删除该配置
  • 处理loading状态,避免重复提交
  • 添加文件非空校验,提升用户体验

修改后的前端handleSubmit方法:

const handleSubmit = async () => {
  if (!file) {
    toast({ description: "请选择要上传的文件" });
    return;
  }
  setIsLoading(true);
  try {
    const formData = new FormData();
    formData.append('file', file);

    // 移除手动设置的Content-Type,axios会自动处理
    const response = await axios.post('/api/ingest', formData);
    
    if (response.status === 200) {
      console.log('File uploaded successfully!');
      toast({ description: "文件上传成功" });
    }
  } catch (error) {
    console.log('Error uploading file:', error);
    toast({ description: "文件上传失败,请重试" });
  } finally {
    setIsLoading(false);
  }
}

3. 补充说明

浏览器中通过FormData上传的文件是File类型(Blob的子类),filePart instanceof Blob判断是成立的,若仍触发格式错误,需检查:

  • containerClient是否已正确初始化
  • 前端是否确实选择了文件再提交

内容的提问来源于stack exchange,提问作者Bhavesh Kumar Sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 21:14:52