csjh
csjh•3mo ago

Using `Deno.FsFile.prototype.readable` with `Response`

I was trying to stream a file into a response with the snippet:
const file = await Deno.open(path);
const resp = new Response(file.readable);
file.close();
return resp;
const file = await Deno.open(path);
const resp = new Response(file.readable);
file.close();
return resp;
but it throws a BadResource error. This makes sense, because closing the file would destroy the associated stream, but if I have to close the file at some point, how can I put it in a Response? afaict any way I do it will either error or leave a leak. I guess I could add a finalizer for the file but that seems silly.
6 Replies
csjh
csjh•3mo ago
fwiw new Response(createReadStream(path)) also works but I would assume the runtime-specific implementation is better in some way oh I guess I could just fetch(path) i forgot file:// is supported
Deno AI Helper
Deno AI Helper•3mo ago
You're correct that closing the file before the response is fully sent would result in a BadResource error, as the stream would no longer be valid. The key here is to ensure that the file is closed only after the response has been fully sent and not before. According to the documentation, when you use file.readable as the body of a Response, Deno takes care of closing the file handle automatically once the response is done sending. This means you don't need to manually close the file handle in your code. Here's the example from the documentation:
async function handler(_req) {
// Open the README.md file available at the root of the repository.
const file = await Deno.open("./README.md");

// Use the `readable` property, which is a `ReadableStream`. This will
// automatically close the file handle when the response is done sending.
return new Response(file.readable);
}

Deno.serve(handler);
async function handler(_req) {
// Open the README.md file available at the root of the repository.
const file = await Deno.open("./README.md");

// Use the `readable` property, which is a `ReadableStream`. This will
// automatically close the file handle when the response is done sending.
return new Response(file.readable);
}

Deno.serve(handler);
In this example, the file.readable is passed directly to the Response constructor, and there is no explicit call to file.close(). The Deno runtime ensures that the file is closed after the stream is consumed. So, in your case, you should be able to simply return the Response with the file.readable stream and not worry about closing the file handle manually. Deno will handle the closing of the file for you once the response has been fully sent. 🦕
csjh
csjh•3mo ago
oop I'm silly
Doctor 🤖
Doctor 🤖•3mo ago
Good bot On a side note, if Deno didn't handle auto closing after the readable stream was finished being consumed you could detect that it's done via piping through a transform stream calling file.close() when the flush function is called.
Esente
Esente•3mo ago
That's the same way I would do. But I think using will become a thing soon, and the FileFS will support that.
Doctor 🤖
Doctor 🤖•3mo ago
Using wouldn't work in this context as the dispose function would be called when the object scope ends. Which is the same as calling file.close after placing it in the response and not after it's done sending.