Handle file uploads in SvelteKit, the sane way -

3 min read

Written by Odyssey346

606 words


Handle file uploads in SvelteKit, the sane way #

I was working on a project which required file uploads. I was not sure how to do it, because there wasn’t a whole lot of documentation. I found an article, but it did not use form file (which is what I wanted), so I figured out how to do it myself.

Let’s get started #

First, we need to create a form in your page. It should look something like this:

1
2
3
4
<form action="?/upload" method="post" enctype="multipart/form-data">
  <input type="file" name="file" />
  <button type="submit">Upload</button>
</form>

The form requires multipart/form-data or else SvelteKit won’t be able to handle it.

Now, create a file called +page.server.ts (or .js, depending on what you chose for your project. I assume you are using TypeScript) in the same folder as your page. It should look something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import type { Actions } from "./$types";
import { writeFile, existsSync, mkdir } from 'fs';
import { fail } from "@sveltejs/kit";

export const actions: Actions = {
    submit: async ({ request, getClientAddress }) => {
        const data = await request.formData();
        const file = data.get("file") as File;

        // if there isn't a file or the file is empty, return an error
        if (!file || file.size === 0) {
            return fail(400, { error: true, message: "No file was provided" });
        } else {
            console.log(file);
            // if the file is bigger than 10MB, return an error
            if (file.size > 10000000) {
                return fail(400, { error: true, message: "File is too big. Max 10MB" });
            }
            const fileType = file.type;
            const filename = file.name.split(".")[0];
            // fileType is a MIME type, so do it like this
            if (fileType !== "image/png" && fileType !== "image/jpeg" && fileType !== "image/gif" && fileType !== "image/webp" && fileType !== "video/mp4" && fileType !== "video/webm") {
                return fail(400, { error: true, message: "File type is not supported. \"" + file.type + "\""});
            }

            // get file data
            const fileData = await file.arrayBuffer();
            // split MIME type in two
            const fileExtension = fileType.split("/")[1];
            // print filename
            console.log(filename + "." + fileExtension)

            // print path
            console.log(`./static/uploads/${filename}.${fileExtension}`)
            // check if the uploads folder exists, if not, create it
            if (!existsSync(`./static/uploads`)) {
                mkdir(`./static/uploads`, (err) => {
                    return fail(500, { error: true, message: "Something went wrong while saving the file you uploaded. Error: " + err});
                })
            };
            // write file
            writeFile(`./static/uploads/${filename}.${fileExtension}`, Buffer.from(fileData), (err) => {
                if (err) {
                    return fail(500, { error: true, message: "Something went wrong while saving the file you uploaded. Error: " + err});
                }
            });


            // return success
            return { success: true, message: "File uploaded successfully"}
        }
    },
};

Now in your +page.svelte, we can use the data returned from the form action. You need to import import type { PageData, ActionData } from "./$types";, and then create a variable like this: export let form: ActionData;. This allows you to use the data returned from the form action. Here’s a complete example of what your +page.svelte should look like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<script lang="ts">
        import type { PageData, ActionData } from "./$types";
        export let form: ActionData;
</script>

<form action="?/upload" method="post" enctype="multipart/form-data">
  <input type="file" name="file" />
  <button type="submit">Upload</button>
</form>

{#if form?.error}
<p>{form?.message}</p>
{:else if form?.success}
<p>{form?.message}</p>
{/if}

Pretty simple, right? Now file uploads should work in your SvelteKit app!

← Previous post