Streaming large remote files


#1

Hello! Is it possible to stream large files through Worker? I always get ‘script exceeded time limit’.

addEventListener(“fetch”, event => {
event.respondWith(fetchAndStream(event.request))
});

async function fetchAndStream() {
    // Fetch from origin server.
    let response = await fetch('https://www.dropbox.com/s/9x3yzowigc0sswh/alfa.mp4?dl=1');

    // Create an identity TransformStream (a.k.a. a pipe).
    // The readable side will become our new response body.
    let { readable, writable } = new TransformStream();

    // Start pumping the body. NOTE: No await!
    streamBody(response.body, writable);

    const responseInit = {
        headers: {
            'Content-Type': 'video/mp4',
            'Content-Disposition' : 'attachment; filename="video.mp4"'
        }
    };

    // ... and deliver our Response while that's running.
    return new Response(readable, responseInit)
}

async function streamBody(readable, writable) {
    let reader = readable.getReader();
    let writer = writable.getWriter();

    while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        // Optionally transform value's bytes here.
        await writer.write(value);
    }

    await writer.close();
}

#2

My first question is, do you actually need a TransformStream? Are you hoping to transform the response, or are you just looking for the response to be passed to the original request?


#3

I need to change response headers and pass it to the original request.


#4

You don’t need TransformStream for that, it’s needed only to modify actual body. Instead, you can pass response.body to the new Response constructor directly.


#5

Thank you. The code below works perfectly. Is this usage of the Worker allowed by Cloudflare? I mean to stream remote files through the Worker.

addEventListener("fetch", event => {
  event.respondWith(fetchAndStream(event.request))
});

async function fetchAndStream() {
    // Fetch from origin server.
    let response = await fetch('https://www.dropbox.com/s/9x3yzowigc0sswh/alfa.mp4?dl=1');

    const responseInit = {
        headers: {
            'Content-Type': 'video/mp4',
            'Content-Disposition' : 'attachment; filename="video.mp4"'
        }
    };

    // ... and deliver our Response while that's running.
    return new Response(response.body, responseInit)
}

#6

@jiri, yes, this is no problem, and an intended use case.


#7

Great, thank you.