Node.js服务端实现全格式Excel文件上传下载至Azure Blob Storage及问题解决
上传问题根因
blockBlobClient.upload()方法的第一个入参要求传入文件实际内容(支持Buffer、可读流、二进制字符串类型),SDK不会自动解析传入的路径为本地文件,所有传入该方法的内容都会被直接作为Blob的存储数据。当前代码传入的是拼接后的本地临时文件路径字符串,因此Blob存储的仅为该路径文本,不是Excel的实际二进制内容。- 未对multer生成的临时磁盘文件做内容读取,直接将路径作为上传数据源传递。
上传逻辑修复步骤
- 引入Node.js内置
fs模块的文件流读取能力,读取临时文件的二进制内容 - 显式指定Blob的HTTP头,匹配Excel文件的MIME类型,避免下载时类型识别异常
- 移除upload入参中无意义的容器名拼接逻辑
修复后的上传核心代码:
import { DefaultAzureCredential } from '@azure/identity'; import { BlobServiceClient } from '@azure/storage-blob'; // 新增:引入fs模块的文件流方法 import { createReadStream } from 'fs'; const account = process.env.ACCOUNT_NAME; const containerName = process.env.CONTAINER_NAME; if ( !process.env.AZURE_TENANT_ID || !process.env.AZURE_CLIENT_ID || !process.env.AZURE_CLIENT_SECRET || !account || !containerName ) { return next(SystemResponse.badRequestError('Azure AD authentication information not provided, but it is required to run this sample. Exiting.')); } const defaultAzureCredential = new DefaultAzureCredential(); const blobServiceClient = new BlobServiceClient( `https://${account}.blob.core.windows.net`, defaultAzureCredential, ); const containerClient = blobServiceClient.getContainerClient(containerName); // 生成Blob名称的逻辑保持不变 const blobName = `${req.files.file.name.split('.')[0]}+${new Date().getTime()}.${req.files.file.name.split('.')[1]}`; const blockBlobClient = containerClient.getBlockBlobClient(blobName); // 替换原错误的upload调用 const fileStream = createReadStream(req.files.file.path); const uploadBlobResponse = await blockBlobClient.upload( fileStream, req.files.file.size, { // 显式指定Excel的Content-Type blobHTTPHeaders: { blobContentType: req.files.file.mimetype } } );
如果使用multer的内存存储模式(无临时磁盘文件),可直接将
req.files.file.buffer作为upload的第一个入参传入,无需额外读取文件流。
下载逻辑修复
当前下载仅返回文件名,是因为未正确将Blob的二进制流通过HTTP响应输出,需按以下逻辑调整:
- 从Blob获取读取流
- 设置正确的HTTP响应头,告知客户端返回的是附件下载
- 将Blob流管道到响应对象,直接输出二进制内容
示例下载代码:
// 传入对应Blob的名称获取客户端 const targetBlobClient = containerClient.getBlockBlobClient(targetBlobName); const downloadRes = await targetBlobClient.download(); // 设置响应头,触发浏览器文件下载 res.setHeader('Content-Type', downloadRes.contentType); res.setHeader( 'Content-Disposition', `attachment; filename="${encodeURIComponent(targetBlobName)}"` ); res.setHeader('Content-Length', downloadRes.contentLength); // 将Blob的可读流直接输出到响应 downloadRes.readableStreamBody.pipe(res);
内容的提问来源于stack exchange,提问作者Hemendra
相关产品推荐
相关产品推荐

