如何在Next.js中将文件上传至Azure Blob存储?
从零搭建Azure Blob存储并实现图片上传(替换AWS方案)
一、创建Azure Blob存储资源
- 登录Azure门户,搜索「存储账户」并点击「创建」
- 填写基础配置:选择订阅、新建/选择资源组、设置全局唯一的存储账户名称、选就近区域、性能默认「标准」、冗余选「本地冗余存储(LRS)」
- 确认配置后点击「查看+创建」,等待资源部署完成
二、创建Blob存储容器
进入刚创建的存储账户详情页:
- 左侧菜单找到「数据存储」下的「容器」,点击「+容器」
- 容器名称设为业务相关的标识(比如
product-images),公共访问级别选「Blob(仅限Blob匿名读取)」(适配电商图片前端直接访问的场景),完成创建
三、获取存储访问凭证
回到存储账户详情页:
- 左侧「安全+网络」菜单选择「访问密钥」,复制其中一个「连接字符串」,保存好后续代码要用到
四、适配前端上传逻辑(结合你的代码)
1. 安装Azure存储SDK依赖
在项目根目录运行命令:
npm install @azure/storage-blob
2. 完善upload.js上传逻辑
替换原AWS相关代码,用Azure SDK实现上传:
import { BlobServiceClient } from "@azure/storage-blob"; // 从环境变量读取连接字符串,禁止硬编码 const AZURE_STORAGE_CONNECTION_STRING = process.env.NEXT_PUBLIC_AZURE_STORAGE_CONNECTION_STRING; export async function uploadImage(file) { if (!AZURE_STORAGE_CONNECTION_STRING) { throw new Error("未配置Azure存储连接字符串"); } // 初始化Blob服务客户端 const blobServiceClient = BlobServiceClient.fromConnectionString(AZURE_STORAGE_CONNECTION_STRING); const containerClient = blobServiceClient.getContainerClient("product-images"); // 对应你创建的容器名 // 生成唯一文件名,避免重复覆盖 const fileName = `${Date.now()}-${file.name.replace(/\s+/g, "-")}`; const blockBlobClient = containerClient.getBlockBlobClient(fileName); // 上传文件并保留原MIME类型 await blockBlobClient.uploadData(file, { blobHTTPHeaders: { blobContentType: file.type, }, }); // 返回图片的访问URL return blockBlobClient.url; }
注意:在Next.js项目根目录创建
.env.local文件,写入NEXT_PUBLIC_AZURE_STORAGE_CONNECTION_STRING="你的连接字符串",重启项目生效
3. 在productForm.js中接入上传功能
在你的组件中添加文件上传处理逻辑:
import { useEffect, useState } from "react"; import { useRouter } from "next/router"; import axios from "axios"; import Spinner from "@/components/Spinner"; import { ReactSortable } from "react-sortablejs"; import { uploadImage } from "@/path/to/upload"; // 替换为你的upload.js实际路径 export default function ProductForm({ _id, title: existingTitle, description: existingDescription, price: existingPrice, images: existingImages, category: assignedCategory, properties: assignedProperties, }) { const [title, setTitle] = useState(existingTitle || ""); const [description, setDescription] = useState(existingDescription || ""); const [price, setPrice] = useState(existingPrice || ""); const [images, setImages] = useState(existingImages || []); const [isUploading, setIsUploading] = useState(false); // 处理图片上传 const handleFileUpload = async (e) => { const files = Array.from(e.target.files); setIsUploading(true); try { const uploadedUrls = await Promise.all( files.map(file => uploadImage(file)) ); setImages(prev => [...prev, ...uploadedUrls]); } catch (err) { console.error("上传失败:", err); alert("图片上传失败,请重试"); } finally { setIsUploading(false); } }; // 原教程的表单提交、图片排序等逻辑保留不变 return ( <div> {/* 其他表单字段 */} <div className="mt-4"> <label className="block mb-1">产品图片</label> <input type="file" multiple accept="image/*" onChange={handleFileUpload} disabled={isUploading} className="border p-2 rounded" /> {isUploading && <Spinner />} <ReactSortable list={images} setList={setImages} className="flex flex-wrap gap-2 mt-2" > {images.map((img, idx) => ( <img key={idx} src={img} alt={`产品图片 ${idx+1}`} className="w-24 h-24 object-cover rounded border" /> ))} </ReactSortable> </div> {/* 提交按钮等 */} </div> ); }
五、安全提示
- 永远不要把连接字符串硬编码到代码中,必须用环境变量管理
- 如果担心前端暴露凭证,可改为后端签名上传模式(适合高安全要求场景)
- 可在Azure存储账户中配置Blob生命周期规则,自动清理临时无用图片
内容的提问来源于stack exchange,提问作者user21159731
相关产品推荐
相关产品推荐

