2

I want to build a file sharing site kinda like nextcloud. The Idea is that their are files on a server (currently they are local), the svelte backend parses through them and presents them. You than can click through the folders to find the files you want and when you click on a file you can download this file.

Now parsing through the directory is working just fine. But i can't seem to figure out how to create an endpoint that returns a file for download.

The files should not be part of sveltekit so they are not static files, so i can't just download them by pointing a url to them.

So I tried to create a blob according to the filetype but i can't seem to work out how this really works.

Thats what i have so far for the endpoint.

import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import fs from "fs";
import { resolve } from 'path';

export const GET = (async (event) => {
    const filePath = resolve("."+event.url.pathname);
    console.log(filePath);
    var file = fs.readFileSync(filePath);

    return json({
        status:200,
        headers: {
        "Content-type" : "application/pdf",
        "Content-Disposition": `attachment; filename=${event.url.pathname.split("/").pop()}`
        },
        body: file
    });
}) satisfies RequestHandler;
Peppi
  • 51
  • 3
  • It shouldn't matter that the files you want to download are not part of your SvelteKit project as long as the remote server account handling requests has a read access to the directory(ies) where the files are stored. Then you should still be able to recreate URLs on the fly and make it easy for users to download the files. – Thomas Hennes Feb 02 '23 at 20:06

1 Answers1

3

You are not returning JSON, that is for structured text based data, instead return a raw Response object which can contain whatever you want.

The content type for raw binary data should be application/octet-stream. I also would just pass the file as a query string argument. E.g.

export const GET: RequestHandler = async ({ url }) => {
    const name = url.searchParams.get('name'); // E.g. /files/download?name=X
    if (name == null)
        return new Response('No name provided', { status: 500 });

    const buffer = await readFile(`./files/${name}`);

    return new Response(
        buffer,
        {
            status: 200,
            headers: {
                'Content-Type': 'application/octet-stream',
                'Content-Disposition':
                    // Use filename* instead of filename to support non-ASCII characters
                    `attachment; filename*=UTF-8''${encodeURIComponent(name)}`,
            },
        }
    );
};

Make sure to also encode the name query parameter value when constructing the URLs for the page using encodeURIComponent or by constructing URLs with the URL class. E.g.

{#each data.files as file}
  <a href="/files/download?name={encodeURIComponent(file)}">
    {file}
  </a>
{/each}
H.B.
  • 166,899
  • 29
  • 327
  • 400