Firebase Function报错:无法打开文件写入,缩略图生成失败
Let's break down what's causing the "unable to open for write" error in your function and fix it step by step:
Key Issues in Your Code
Incorrect path for sharp's
toFile
You're trying to write the resized image directly tothumbFilePath, which is a cloud storage path (likeimages/thumb_photo.jpg), not a local filesystem path. The sharp library can't write to cloud storage directly—it needs a local temporary file path.Missing
awaiton sharp'stoFile
ThetoFilemethod is asynchronous, but you commented out theawaitkeyword. This means your code tries to upload the file before sharp finishes resizing it, leading to issues with the file state.Uploading the wrong file to the wrong destination
You're uploading the original temp file (tempFilePath) back to the original cloud path (filePath) instead of uploading the resized thumbnail to thethumbFilePathcloud location.Synchronous unlink with
fs.unlinkSync
Using synchronous filesystem operations in an async function can block the event loop. It's better to use the promise-basedfs.promises.unlinkinstead.
Corrected Code
const functions = require('firebase-functions'); const admin = require('firebase-admin'); admin.initializeApp() const {Storage} = require("@google-cloud/storage"); const gcs = new Storage(); const path = require('path'); const os = require('os'); const fs = require('fs').promises; // Use promise-based fs const sharp = require("sharp"); exports.generateThumbnail = functions.storage.object().onFinalize(async (object) => { const fileBucket = object.bucket; const filePath = object.name; const contentType = object.contentType; const metageneration = object.metageneration; if (!contentType.startsWith('image/')) { return console.log('This is not an image.'); } const fileName = path.basename(filePath); if (fileName.startsWith('thumb_')) { return console.log('Already a Thumbnail.'); } const bucket = admin.storage().bucket(fileBucket); const tempFilePath = path.join(os.tmpdir(), fileName); // Create a temp path for the resized thumbnail const tempThumbPath = path.join(os.tmpdir(), `thumb_${fileName}`); console.log('Created temporary path', tempFilePath); const metadata = { contentType: contentType, }; // Download original image to temp location await bucket.file(filePath).download({destination: tempFilePath}); console.log('Image downloaded locally to', tempFilePath); const thumbFileName = `thumb_${fileName}`; const thumbFilePath = path.join(path.dirname(filePath), thumbFileName); console.log('Target thumbnail path in cloud storage', thumbFilePath); const size = 200; // Wait for sharp to resize and write to local temp thumb path await sharp(tempFilePath).resize(size, size).toFile(tempThumbPath); console.log('Thumbnail generated locally at', tempThumbPath); // Upload the resized thumbnail to cloud storage await bucket.upload(tempThumbPath, { destination: thumbFilePath, metadata: metadata, }); console.log('Thumbnail uploaded to cloud storage'); // Clean up temp files await fs.unlink(tempFilePath); await fs.unlink(tempThumbPath); return console.log('Temporary files cleaned up'); });
Additional Notes
- Always ensure you're working with local temporary paths (from
os.tmpdir()) when doing filesystem operations in Firebase Functions—you don't have write access to arbitrary cloud paths locally. - Use async/await consistently for all asynchronous operations (like sharp, bucket uploads, filesystem actions) to avoid race conditions.
- Adding console logs at each step helps debug where things might be going wrong in the function execution.
内容的提问来源于stack exchange,提问作者Mantofka

