When using App Router how can I return a file from an api route?
Answered
jsoneaday posted this in #help-forum
So far in my app/api/route.ts file I created this code which does not work. I get 500.
export async function GET(request: Request, response: NextApiResponse) {
const fileStream = createReadStream(
join(fileDir, "pexels-alotrobo-1653080.jpg")
);
const result: any = [];
return new Promise((res, rej) => {
fileStream
.pipe(response)
.on("data", (data) => {
result.push(data);
})
.on("end", () => {
res(result);
})
.on("error", rej);
});
}Answered by joulev
very good question.
the answer depends on what you want to do.
for small files, you can simply read the buffer of the file and return it in the response.
for larger files, you might need to stream it which is a bit more complicated, you might want to create an utility function for this.
the answer depends on what you want to do.
for small files, you can simply read the buffer of the file and return it in the response.
import { readFile } from "node:fs/promises";
export async function GET() {
const FILE = "/Users/joulev/Desktop/mrt-walk.png";
const buffer = await readFile(FILE);
return new Response(buffer, { headers: { "Content-Type": "image/png" } });
}for larger files, you might need to stream it which is a bit more complicated, you might want to create an utility function for this.
// Credits to https://github.com/vercel/next.js/discussions/15453#discussioncomment-6589193
import { createReadStream } from "node:fs";
import { stat } from "node:fs/promises";
export async function GET() {
const FILE = "/Users/joulev/Desktop/foo.mov";
const downloadStream = createReadStream(FILE);
const stream = new ReadableStream({
start(controller) {
downloadStream.on("data", (chunk: Buffer) => controller.enqueue(new Uint8Array(chunk)));
downloadStream.on("end", () => controller.close());
downloadStream.on("error", (error: NodeJS.ErrnoException) => controller.error(error));
},
cancel() {
downloadStream.destroy();
},
});
const stats = await stat(FILE);
return new Response(stream, {
headers: { "Content-Type": "video/quicktime", "content-length": `${stats.size}` },
});
}4 Replies
I just tried switching the route handler to this
export async function GET(request: Request, response: NextApiResponse) {
const files: Buffer[] = getTestImgFiles();
response.setHeader("Content-Type", "image/jpeg");
response.send(files[0]);
} And now I get error that response.setHeader is not a function? TypeError: response.setHeader is not a function
at GET (webpack-internal:///(rsc)/./src/app/api/[id]/route.ts:10:14)@jsoneaday So far in my app/api/route.ts file I created this code which does not work. I get 500. typescript
export async function GET(request: Request, response: NextApiResponse) {
const fileStream = createReadStream(
join(fileDir, "pexels-alotrobo-1653080.jpg")
);
const result: any = [];
return new Promise((res, rej) => {
fileStream
.pipe(response)
.on("data", (data) => {
result.push(data);
})
.on("end", () => {
res(result);
})
.on("error", rej);
});
}
very good question.
the answer depends on what you want to do.
for small files, you can simply read the buffer of the file and return it in the response.
for larger files, you might need to stream it which is a bit more complicated, you might want to create an utility function for this.
the answer depends on what you want to do.
for small files, you can simply read the buffer of the file and return it in the response.
import { readFile } from "node:fs/promises";
export async function GET() {
const FILE = "/Users/joulev/Desktop/mrt-walk.png";
const buffer = await readFile(FILE);
return new Response(buffer, { headers: { "Content-Type": "image/png" } });
}for larger files, you might need to stream it which is a bit more complicated, you might want to create an utility function for this.
// Credits to https://github.com/vercel/next.js/discussions/15453#discussioncomment-6589193
import { createReadStream } from "node:fs";
import { stat } from "node:fs/promises";
export async function GET() {
const FILE = "/Users/joulev/Desktop/foo.mov";
const downloadStream = createReadStream(FILE);
const stream = new ReadableStream({
start(controller) {
downloadStream.on("data", (chunk: Buffer) => controller.enqueue(new Uint8Array(chunk)));
downloadStream.on("end", () => controller.close());
downloadStream.on("error", (error: NodeJS.ErrnoException) => controller.error(error));
},
cancel() {
downloadStream.destroy();
},
});
const stats = await stat(FILE);
return new Response(stream, {
headers: { "Content-Type": "video/quicktime", "content-length": `${stats.size}` },
});
}Answer
Awesome! So if I add this to an image tag will that run on server or do I need "use client"?
@jsoneaday Awesome! So if I add this to an image tag will that run on server or do I need "use client"?
the image tag sends the request to the server so yeah it will run on the server