typescriptsveltekitimgur

Sending image to Imgur using Imgur API


I am using svelte kit and typescript for my project. I want to be able to save photos that the user sends via the uploadPhoto/+page.svelte I have created:

<script lang="ts">
    import { Button } from 'sveltestrap/src';

    const dropHandler = (ev: DragEvent) => {
        console.log('File(s) dropped');

        // Prevent default behavior (Prevent file from being opened)
        ev.preventDefault();
        if (ev.dataTransfer) {
            if (ev.dataTransfer.items) {
                // Use DataTransferItemList interface to access the file(s)
                [...ev.dataTransfer.items].forEach((item, i) => {
                    // If dropped items aren't files, reject them
                    if (item.kind === 'file') {
                        const file = item.getAsFile();
                        if (file) {
                            console.log(`… file[${i}].name = ${file.name}`);
                            const input = document.getElementById("img") as HTMLInputElement;
                            input.files = [file];
                        }
                    }
                });
            } else {
                // Use DataTransfer interface to access the file(s)
                [...ev.dataTransfer.files].forEach((file, i) => {
                    console.log(` file[${i}].name = ${file.name}`);
                    const input = document.getElementById("img") as HTMLInputElement;
                    input.files = [file];
                });
            }
        }
    };

    const dragOverHandler = (ev: DragEvent) => {
        console.log('File(s) in drop zone');

        // Prevent default behavior (Prevent file from being opened)
        ev.preventDefault();
    };

    const submitHandler = async (ev: Event) => {
        ev.preventDefault();
        const formData = new FormData();
        const file = (document.getElementById("img") as HTMLInputElement).files![0];
        formData.append("img", file);
        const response = await fetch("/uploadPhoto?/upload", {
            method: "POST",
            body: formData
        });
        const result = await response.json();
        console.log(result);
    };
</script>
<div class="center-items" style="top:10%;position:absolute">
    <h1>Upload Photo</h1>
    <br />
    <p>Here you can upload a picture of the night sky and identify the stars you have captured.</p>
    <p>
        After submiting a picture via the drag-and-drop or by selecting a picture, wait for the map to
        load in order to see the stars
    </p>

    <br />
    <div id="form"  style="top: 15%;width:auto;height:max-content;">
        <div id="drop_zone" on:drop={dropHandler} on:dragover={dragOverHandler}>
            <label for="img">Select image:</label>
            <input type="file" id="img" name="img" accept="image/*" />
            <div style="padding-left:80%">
                <Button color="primary" on:click={submitHandler}>Submit</Button>
            </div>

            <p>Drag one file to this <i>drop zone</i>.</p>
        </div>
    </div>
</div>

<style>
    #drop_zone {
        border: 5px solid blue;
        width: 99%;
        height: 40%;
    }

    @media screen and (max-width: 600px) {
        p {
            padding-left: 10%;
            padding-right: 10%;
            text-align: center;

            letter-spacing: 0.8px;
        }
    }
    #form {
        top: 15%;
        width: 90%;
        height: max-content;
    }
</style>

I know that the form works because I get the file in my uploadPhoto/+page.server.ts:


import { redirect } from '@sveltejs/kit';
import { ImgurClient } from 'imgur';
import type { PageServerLoad, Action, Actions } from './$types';
export const load: PageServerLoad = async ({ locals }) => {
    if (!locals.user) {
        throw redirect(302, 'login');
    }
};
const client = new ImgurClient({
    accessToken: process.env.TOKEN,
    clientId: process.env.CLIENT_ID,
    clientSecret: process.env.CLIENT_SECRET,
    refreshToken: process.env.REFRESH_TOKEN
});

async function uploadToUmgur(file: File) {
    const response = await client.upload({
        image:  file,
        type: 'base64'
    });
    console.log(response.data);
    return response.data;
}

const upload: Action = async ({ request }) => {
    try
    {
        if (request.method === 'POST') {
        const form = await request.formData();
        const image = form.get('img') as File;
        console.log(form.get('img'));
        if (image) {
            console.log(`Received file with name: ${image.name}`);

            return await uploadToUmgur(image);
        }
    }
    }catch(e){
        console.log(e);
    }
    
};



export const actions: Actions = { upload };

My problem is the following. I want to send the file to the API using the base64 format.

The solution I need is to be able to sort of transform the file variable (in uploadToUmgur function) to base64 format and send it to the API.

I have searched for solutions and all they say is to use FileReader or use fs (the nodejs module for FileSystem) but when I use FileReader I get error that FileReader is not defined. I read that fs actually works with files that are saved in the system. However my idea is just to send the file and not store it in the server's system.


Solution

  • You can use newer methods for reading the file; to convert to base 64, read it as a buffer and use its toString function:

    const buffer = Buffer.from(await file.arrayBuffer());
    const base64 = buffer.toString('base64');