Cloud function generating thumbnails doesn't overwrite old thumbnail with the same name

Viewed 653

I'm using cloud functions to resize and generate thumbnails for uploaded images in Firebase Storage. On the first upload the thumbnails are generated but i also want to be able to edit those images while keeping the same name. This is how i'm doing it :

I upload an image with this function on the client :

    uploadImage (imageFile, folderName, imageName){
         const storageRef = firebase.storage().ref();
         // need to prefix image name with "slot_"
         storageRef.child(`${folderName}/slot_${imageName}`).put(imageFile)
    }

Thumbnails are then generated with this cloud function :

export const generateThumbs = functions.storage.object().onFinalize(async 
    object => {
const bucket = gcs.bucket(object.bucket)
const filePath = object.name;
const fileName = filePath.split('/').pop();
const bucketDir = dirname(filePath);
const slotSizes = [64,250]

const temporaryDirectory = join(tmpdir(), `thumbs_${fileName}`);
const temporaryFilePath = join(temporaryDirectory, 'source.png');

// avoid loop includes only images
if (fileName.includes('thumb_') ||
    !object.contentType.includes('image')) {
    return false;
}

await fileSystem.ensureDir(temporaryDirectory);

// original file in temp directory
await bucket.file(filePath).download({
    destination: temporaryFilePath
});
const slotUploadPromises = slotSizes.map(async size => {
        const thumbName = `thumb_${size}_${fileName}`;
        const thumbPath = join(temporaryDirectory, thumbName);

        await sharp(temporaryFilePath).resize(size, size).toFile(thumbPath);

        return bucket.upload(thumbPath, {
            destination: join(bucketDir, thumbName),
            metadata: {
                contentType: 'image/jpeg',
            }
        })
    });


await Promise.all(slotUploadPromises)
// removes temp directory
return fileSystem.remove(temporaryDirectory);

So if i call uploadImage(appleImage, 'MyImages', 'test') i'll have in my storage folder 3 images (naming IS important):

  • slot_test
  • thumb_250_slot_test
  • thumb_64_slot_test

At this point if i call again uploadImage(avocadoImage, 'MyImages', 'test') i'd expect to have in the storage the same "naming structure" but with the updated image in place of the old ones, so the new thumbnails should just overwrite the old ones. What actually happens is that the "base" image gets updated while both thumbnails don't. Ending up with :

  • slot_test (displaying the UPDATED image)
  • thumb_250_slot_test (displaying the thumbnail of the OLD image)
  • thumb_64_slot_test (displaying the thumbnail of the OLD image)

I've logged the cloud function extensively, no errors are thrown from the function during execution, thumbnails are created normally and the firebase console also updates the creation date of the thumbnails but i still get the old thumbnails image. I've tried removing the temporary directory using fs-extra emptyDir(), i've tried to remove every single thumbnail first (via client) and then uploading again with no luck.

EDIT : Found a solution to my problem by NOT creating any temporary folder or temporary files and using sharp pipeline instead. That said i'm still missing the underlying problem in the code above. I'm quite convinced that, for whatever reason, the function didn't remove the temporary folder and that was generating problems whenever i tried to overwrite the images. This function works :

export const generateThumbs = functions.storage.object().onFinalize(async object => {
const bucket = gcs.bucket(object.bucket)
const filePath = object.name;
const fileName = filePath.split('/').pop();
const bucketDir = dirname(filePath);

// metadata file
const metadata = {
    contentType: 'image/jpeg',
}

if (fileName.includes('thumb_') || !object.contentType.includes('image')) {
    return false;
}

if (fileName.includes('slot')) {
    // array of promises
    const slotUploadPromises = slotSizes.map(async size => {
        const thumbName = `thumb_${size}_${fileName}`;
        const thumbPath = join(path.dirname(filePath), thumbName);
        const thumbnailUploadStream = bucket.file(thumbPath).createWriteStream({ metadata });

        const pipeline = sharp();

        pipeline.resize(size, Math.floor((size * 9) / 16)).max()
            .pipe(thumbnailUploadStream);

        bucket.file(filePath).createReadStream().pipe(pipeline);

        return new Promise((resolve, reject) => 
               thumbnailUploadStream
               .on('finish', resolve)
               .on('error', reject));
    });
    await Promise.all(slotUploadPromises)
}
0 Answers
Related