Firebase存储上传缩略图卡在0.0%无报错,求排查方案
问题
我正在实现一个包含以下步骤的图片上传函数:
- 使用sharp.js压缩本地图片并存入缓存文件夹;
- 将原图上传至Firebase Storage指定位置;
- 将压缩后的缩略图上传至Firebase Storage另一位置。
当前步骤1、2可正常执行,但步骤3始终停滞:缩略图上传进度卡在0.0%,且Firebase无任何错误抛出。我已添加大量日志和catch语句仍无法定位原因。
实现代码
// appends a filename to the end of a storageReference export function addFilename(filename: string, reference: StorageReference) { return ref(storage, `${reference}/${path.basename(filename)}`); } // -- file uploading export async function uploadFromDisk( filePath: string, destination: StorageReference, metadata?: UploadMetadata, ) { console.log("initiating upload - " + JSON.stringify(filePath)); const taskPromise: Promise<UploadTaskSnapshot> = new Promise(((resolve, reject) => { const task = uploadBytesResumable( addFilename(filePath, destination), fs.readFileSync(filePath), metadata ); task.on("state_changed", (snapshot) => { if (snapshot.state == "running") { console.log(`File ${filePath} status: ${(snapshot.bytesTransferred / snapshot.totalBytes * 100).toPrecision(2)}%`); } if (snapshot.bytesTransferred >= snapshot.totalBytes ) { printCyan(`File ${filePath} available @ ${snapshot.ref.bucket}/${snapshot.ref.fullPath}`); resolve(snapshot); } if (snapshot.state == "error") { reject(new Error("There was an error uploading file " + filePath)); } }); })); return taskPromise; } export async function uploadGalleryImage( imagePath: string, site: string, gallery?: string, ) { const metadata = { name: path.basename(imagePath), contentType: path.extname(imagePath) == ".jpg" ? "image/jpeg" : "image/png" }; const imageFolder = `galleries/${gallery}/full`; const thumbPath = (await compressImages([imagePath]))[0]; const thumbFolder = `galleries/${gallery}/thumbs`; const results: UploadResult[] = []; const imageResult = await uploadFromDisk( imagePath, getAssetFolder(site, imageFolder), metadata, ); const thumbResult = await uploadFromDisk( thumbPath, getAssetFolder(site, thumbFolder), metadata, ); results.push(imageResult); results.push(thumbResult); return results; }
控制台输出示例
File images/bulksplash-charleingracia--Ux5mdMJNEA.jpg status: 77% // original image is uploading fine ... File images/bulksplash-charleingracia--Ux5mdMJNEA.jpg status: 1.0e+2% File images/bulksplash-charleingracia--Ux5mdMJNEA.jpg available ... // fully uploaded original image ... File thumbs/bulksplash-charleingracia--Ux5mdMJNEA.jpg status: 0.0% // stuck at 0.0% File images/bulksplash-davieees-uJDWga_IOck.jpg status: 35% // the other, previously initiated image uploads are chugging along just fine File images/bulksplash-ferminrp-a1dpvJv45To.jpg status: 79% File images/bulksplash-aliciasteels-mrB45cHDsrM.jpg status: 55%
排查方向与修复建议
1. 验证缩略图文件的完整性
- 在获取
thumbPath后添加日志,打印文件路径和fs.statSync(thumbPath).size的结果。如果文件大小为0,说明sharp.js压缩未正确生成文件,需排查compressImages函数的异步写入逻辑,确保等待sharp().toFile()完成后再返回路径。 - 手动检查
thumbPath指向的文件是否可正常打开,排除文件损坏问题。
2. 修复上传Promise的逻辑漏洞
当前uploadFromDisk的Promise仅在snapshot.bytesTransferred >= snapshot.totalBytes时resolve,若Firebase的上传完成状态未触发该条件,会导致Promise永久pending。改用state_changed的完整回调参数处理:
export async function uploadFromDisk( filePath: string, destination: StorageReference, metadata?: UploadMetadata, ) { console.log("initiating upload - " + JSON.stringify(filePath)); return new Promise<UploadTaskSnapshot>((resolve, reject) => { const task = uploadBytesResumable( addFilename(filePath, destination), fs.readFileSync(filePath), metadata ); task.on("state_changed", (snapshot) => { if (snapshot.state === "running") { const progress = (snapshot.bytesTransferred / snapshot.totalBytes * 100).toPrecision(2); console.log(`File ${filePath} status: ${progress}%`); } }, (error) => { reject(new Error(`Upload failed for ${filePath}: ${error.message}`)); }, () => { const snapshot = task.snapshot; printCyan(`File ${filePath} available @ ${snapshot.ref.bucket}/${snapshot.ref.fullPath}`); resolve(snapshot); } ); }); }
3. 为缩略图单独生成元数据
当前复用原图的metadata可能导致contentType不匹配(比如压缩后格式变化),为缩略图单独生成元数据:
// 在uploadGalleryImage中修改 const thumbMetadata = { name: path.basename(thumbPath), contentType: path.extname(thumbPath) === ".jpg" ? "image/jpeg" : "image/png" }; // 上传缩略图时使用thumbMetadata const thumbResult = await uploadFromDisk( thumbPath, getAssetFolder(site, thumbFolder), thumbMetadata, );
4. 检查Firebase存储路径与权限
- 打印
getAssetFolder(site, thumbFolder)的fullPath属性,确认存储路径拼写、大小写是否正确。 - 验证Firebase Storage安全规则是否允许写入
galleries/${gallery}/thumbs路径,排除权限拦截问题。
5. 排查文件读取的时序问题
fs.readFileSync是同步读取,若compressImages未完全写入缩略图就返回路径,会读取到空内容。确保compressImages函数内部等待sharp的写入操作完成后再返回thumbPath。
内容的提问来源于stack exchange,提问作者ajazz
相关产品推荐
相关产品推荐

