Next.js 13上传文件至Azure Blob Storage时Blob名称异常求助
问题解决:Next.js 13上传文件到Azure Blob Storage时保留原文件名并避免覆盖
问题分析
当前代码存在两个核心问题:
- 前端未将原文件名传递给后端,导致后端无法获取正确的文件名作为Blob名称
- 后端错误尝试访问
request.blob.name(Request对象无此属性),Blob名称异常,重复上传时会覆盖旧文件
前端组件修改(Next.js 13)
修改思路:保留选中的原始文件对象,用FormData传递文件和文件名,避免使用DataURL(会增加体积且丢失原始文件信息)
"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"; export default function UploadData() { // 保存选中的文件对象,而非DataURL const [selectedFile, setSelectedFile] = useState<File | null>(null); const { toast } = useToast(); // 处理文件选择 const handleSelectFile = (event: React.ChangeEvent<HTMLInputElement>) => { const file = event.target.files?.[0]; if (file) { setSelectedFile(file); } }; // 处理文件提交 const handleSubmit = async () => { if (!selectedFile) { toast({ variant: "destructive", title: "请先选择文件" }); return; } try { const formData = new FormData(); formData.append("file", selectedFile); formData.append("fileName", selectedFile.name); const response = await fetch("/api/ingest", { method: "POST", body: formData, // 无需手动设置Content-Type,浏览器会自动生成multipart/form-data边界 }); if (!response.ok) throw new Error("上传失败"); toast({ title: "文件上传成功" }); setSelectedFile(null); } catch (error: any) { toast({ variant: "destructive", title: "上传失败", description: error.message || "未知错误", }); } }; return ( <> <Dialog> <DialogTrigger asChild> <Button>上传文件</Button> </DialogTrigger> <DialogContent className="sm:max-w-[425px]"> <DialogHeader> <DialogTitle>上传文件</DialogTitle> <DialogDescription>选择要上传的文本或PDF文件</DialogDescription> </DialogHeader> <div className="grid gap-4 py-4"> <div className="grid grid-cols-4 items-center gap-4"> <Label htmlFor="file">选择文件</Label> <Input id="file" type="file" accept=".txt,.pdf" // 限制文件类型 onChange={handleSelectFile} className="col-span-full" /> {/* 可选:显示选中的文件名 */} {selectedFile && ( <p className="col-span-full text-sm text-gray-500"> 已选择:{selectedFile.name} </p> )} </div> </div> <DialogFooter> <DialogClose asChild> <Button type="button" onClick={handleSubmit} disabled={!selectedFile}> 提交上传 </Button> </DialogClose> </DialogFooter> </DialogContent> </Dialog> </> ); }
后端API修改(Next.js 13 Route Handler)
修改思路:从FormData中获取文件和文件名,创建BlobClient时使用原文件名;同时添加防覆盖逻辑(文件名后加时间戳)
import { NextResponse } from "next/server"; import { BlobServiceClient } from "@azure/storage-blob"; // 连接Azure Storage账户 const blobService = new BlobServiceClient( `https://${process.env.AZURE_STORAGE_ACCOUNT}.blob.core.windows.net/?${process.env.AZURE_STORAGE_SAS_TOKEN}` ); // 连接容器 const containerClient = blobService.getContainerClient( process.env.AZURE_BLOB_STORAGE_CONTAINER ); // 辅助函数:处理FormData async function parseFormData(request: Request) { const formData = await request.formData(); const file = formData.get("file") as File; const fileName = formData.get("fileName") as string; return { file, fileName }; } // 生成带时间戳的文件名,避免覆盖 function generateUniqueFileName(originalName: string) { const [name, ext] = originalName.split("."); const timestamp = Date.now(); return `${name}_${timestamp}.${ext}`; } export async function POST(request: Request) { try { const { file, fileName } = await parseFormData(request); // 可选:使用原文件名或带时间戳的唯一文件名 const blobName = generateUniqueFileName(fileName); // 防覆盖 // 若不需要防覆盖,直接用 fileName 即可:const blobName = fileName; const blobClient = containerClient.getBlockBlobClient(blobName); // 将File转为ArrayBuffer const buffer = await file.arrayBuffer(); await blobClient.uploadData(buffer, { blobHTTPHeaders: { blobContentType: file.type || "", }, }); return NextResponse.json({ success: true, blobName: blobName, message: "文件上传成功", }); } catch (e) { console.error("上传错误:", e); return NextResponse.json( { success: false, message: "上传失败" }, { status: 500 } ); } }
关键修改说明
前端:
- 保存原始
File对象,保留文件名和文件类型信息 - 使用
FormData传递文件,符合HTTP文件上传规范 - 添加文件类型限制和选中文件提示,提升用户体验
- 保存原始
后端:
- 从
FormData中正确提取文件和文件名 - 新增
generateUniqueFileName函数,通过时间戳生成唯一文件名,彻底解决覆盖问题 - 正确设置Blob的Content-Type,确保Azure存储的文件类型正确
- 从
内容的提问来源于stack exchange,提问作者Bhavesh Kumar Sharma
相关产品推荐
相关产品推荐

