Resolution for photos uploaded via capture-file

We have the app, where users could add attachments to their reports. It uses a component capture-file to add it, since any format can be loaded (pdf or photos/images). However, when you save photos, they are saved in full resolution and weigh quite a lot.

There is a resolution parameter for capture-photo component that is responsible for the quality of the photo. Is there something similar for a capture-file component?

If it cannot be done by setting any parameter for component, could you please suggest another way to compress the images that are obtained when using the component?

Thanks!

@khilda Unfortunately we cannot compress files when uploading files using the <capture-file /> component.

Similarly, we also cannot compress photos when users upload photos to a <capture-photo /> component, as opposed to capturing a new photo using the camera directly from the component.

Basically this is because there is no reliable way to “remove data” from an image or a file after the fact.

The only way to enforce a specific resolution (and file size I guess) is to force users to capture new photos and not allow them to upload from the gallery.

So if you know they are only going to upload PDFs or capture photos, then you can allow them to upload PDFs (but only PDFs) using the <capture-file /> component, and force them to capture photos using the <capture-photo /> component

1 Like

Dear @tielman
I am having the same issue. Since the image can’t not be compress when uploading on the App, is there a possibility to compress the image size when saving in PDF attachment on Cloudcode or reduce the attachment size ?

Thanks

@desiremuson Hi Desire. Nothing built into the JourneyApps architecture at the moment, but I am thinking it should be possible with a NPM package (I haven’t researched this at all though)

If you do find a solution using an NPM lib please let the rest of us know. I will also commit to doing some digging and seeing if I can find one that will work (I just cannot commit to a timeline at this point in time)

Hi @desiremuson and @khilda

To reiterate, it is not possible to compress uploaded files - more details here

Furthermore, in terms of doing it in the backend after the fact I could not find a standard NPM package that allows image compression AND works via CloudCode (this is because of the the image compression libs require access to OS binaries that you cannot access from CloudCode - at least not at time of writing)

However, there are several API services that you can use that accomplishes the same thing.

I tried one, called TinyPNG, as they have a free tier, and the implementation was fairly simple.

From a workflow point of view, if you need to have compressed images for a PDF report or something similar, I would recommend saving a compressed version of the original together with the original. I would create the compressed version “immediately” whenever the original is uploaded using a webhook triggered CC task. I would not do the compression “inline” at the time when you want to generate the PDF.

Your results may vary, but the test case I had reduced a 2.5MB png down to 0.5 MB without having to resize the image. When I added resizing, that number goes down to 0.05MB. See below results and example code.

PS. You will have to review TinyPNG’s terms and usage to determine if you want to make use of their service or not.

Compression Results - from CC

11:29:07.110 [TASK:INFO] Original File Size: 2.473 MB (decimal) | 2.358 MB (binary)
11:29:09.029 [TASK:INFO] Compressed File Size: 0.612 MB (decimal) | 0.597 MB (binary)
11:29:11.074 [TASK:INFO] Resized File Size: 0.059 MB (decimal) | 0.056 MB (binary)
11:29:11.254 [TASK:INFO] Request complete. Response code: 204. Memory used: 76MB.
11:29:11.272 [QUEUE:INFO] Moved from state 'QUEUED' to 'COMPLETED'

CloudCode Code

const tinify = require("tinify");
tinify.key = process.env.TINIFY_KEY
export async function run() {
    // Your code here
    let photoObj = await DB.photo.first("photo != ?", null);

    let photo = photoObj.photo;
    let photoBuffer = await photo.toBuffer();

    let originalSize = photoBuffer.byteLength
    console.log(`Original Size: ${(originalSize/1000/1000).toFixed(3)} MB (decimal) | ${(originalSize/1024/1024).toFixed(3)} MB (binary)`)

    // OPTION 1: Just compression - no resizing (your results may vary)
    const compressedFileBuffer = await tinify.fromBuffer(photoBuffer).toBuffer();
    let compressedSize = compressedFileBuffer.byteLength;

    console.log(`Compressed Size: ${(compressedSize/1000/1000).toFixed(3)} MB (decimal) | ${(compressedSize/1024/1000).toFixed(3)} MB (binary)`)

    let newCompressedFile = await Attachment.create({data: compressedFileBuffer, filename: 'Compressed File.jpg'})
    let newPhoto = DB.photo.create();
    newPhoto.photo = newCompressedFile;
    newPhoto.name = "Compressed Photo";
    await newPhoto.save();

    // OPTION 2: Compression + Resizing (your results may vary)
    let resizeOptions = {
        method: 'fit',
        width: 1200,
        height: 1200
    }

    const resizedFileBuffer = await tinify.fromBuffer(photoBuffer).resize(resizeOptions).toBuffer();
    let resizedSize = resizedFileBuffer.byteLength;

    console.log(`Resized Size: ${(resizedSize/1000/1000).toFixed(3)} MB (decimal) | ${(resizedSize/1024/1024).toFixed(3)} MB (binary)`)

    let newResizedFile = await Attachment.create({data: resizedFileBuffer, filename: 'Resized File.jpg'})
    newPhoto = DB.photo.create();
    newPhoto.photo = newResizedFile;
    newPhoto.name = "Resized Photo";
    await newPhoto.save();
}

I hope this helps