Compress Images Before Upload To Pocketbase Using SvelteKit

1 Comment
Published: 18.06.2023

Do you want to create an image upload with SvelteKit and compress the images before uploading to your storage (for example, Pocketbase)? In this guide, we will do exactly that. We will first have a look at the problem and the final outcome, and then we will develop the solution.

  1. Introduction
  2. Compress images before upload using SvelteKit
  3. Conclusion

Introduction

When using the native file input component, we do not have the possibility to compress uploaded files, in this case, images. In some scenarios, for example, if we allow users to upload a lot of images, compression is necessary to save on storage costs!

We can access the uploaded images in the file input using the bind:files declarative and modify them when we add new images to the input using the on:change event. For the compression itself, we will use the npm package compressorjs and upload the files on the server endpoint of the form action.

In the next step, we will develop exactly that. The styling of the application is done using Pico.css. And you can find the GitHub Repository here.

Compress image before upload using SvelteKit

First, we will install the compressorjs npm package using:

pnpm i compressorjs

Then, we will create a new form that looks like this inside a +page.svelte file:

Need help or want to share feedback? Join my discord community!

<main class="container" style="padding-top: 32px;">
    <h1>Image Upload with compression</h1>
    <form action="?/upload" method="POST">
        <input type="file" name="file" multiple accept="image/*">
        <button type="submit">Upload</button>
    </form>
</main>

In addition to the form itself, we will also create the form action named upload inside a +page.server.ts file. Inside the action, we read the form data and upload the images, if they exist, to our storage e.g., pocketbase:

import { fail } from '@sveltejs/kit';
import type { Actions } from './$types';

export const actions = {
    upload: async ({ request, locals }) => {
        const data = await request.formData();

        // check that the form has files
        const hasImages = data.has('file') && (data.get('file') as File).size > 0;
        if (!hasImages) data.delete('file');

        // upload the image e.g. to pocketbase
        try {
            // const createdRecord = await locals.pocketbase.collection('images').create(data);
            return { success: true }
        }
        catch (error) {
            return fail(500, { error: 'Failed to upload images' });
        }
    }
} satisfies Actions;

Now that the foundation is laid out, we need to compress the images before submitting them to the action. For that, we will use the on:change event. When the event occurs, we will use the compressorjs package to compress the new image. For that, we will have to update our +page.svelte file with the following content:

KOFI Logo

If this guide is helpful to you and you like what I do, please support me with a coffee!

<script lang="ts">
    import Compressor from "compressorjs";

    let files: FileList;

    function compressImage(e: Event) {
        const filesFromElement = (e.target as HTMLInputElement).files;

        if (!filesFromElement) return;
        console.log(filesFromElement);

        for (let i = 0; i < filesFromElement.length; i++) {
            new Compressor(filesFromElement[i], {
                quality: 0.6,
                width: 2048,
                height: 2048,
                resize: "cover",
                success(result: File | Blob) {
                    let file: File;
                    let name = (result as File).name;
                    let type = (result as File).type;
    
                    if (result instanceof Blob) {
                        file = new File([result], "compressed_" + name, { type });
                        console.log(file);
                    } else {
                        file = result as File;
                    }
                    
                    const dt = new DataTransfer();
                    dt.items.add(file);
                    if (filesFromElement) {
                        for (let i = 1; i < filesFromElement.length; i++) {
                            dt.items.add(filesFromElement[i]);
                        }
                    }
                    files = dt.files;
                },
    
                error(err: Error) {
                    console.log(err.message);
                },
            });
        }

    }
</script>

<main class="container" style="padding-top: 32px;">
    <h1>Image Upload with compression</h1>
    <form action="?/upload" method="POST">
        <input type="file" name="file" multiple accept="image/*" bind:files on:change={compressImage}/>
        <button type="submit">Upload</button>
    </form>
</main>

We basically run a compression on all images of the file input. For that, we create a new datastream, add the compressed images and apply them to the files variable. This is necessary because the FileList of the input field is read-only.

When selecting a file, the console output will confirm the compression, as you can see below:

compress images before upload using SvelteKit and Pocketbase: working compression

Conclusion

With that, you created an image upload where we compress the images in SvelteKit before finally uploading them to your file storage, e.g., Pocketbase. I hope this guide was helpful to you. In case you have any questions, feel free to ask!

Don’t miss out on any updates or future guides by subscribing to my monthly newsletter.

[convertkit form=2303042]

Discussion (1)