如标题所示,
在 NodeJs + Express 中,我可以使用以下行返回一个文件作为响应
res.sendFile(absolute_path_to_the_file)
假设我想从 NextJs 目录中的输出文件夹返回单个图像,如何使用 NextJs API 实现此目的?我只能将 res.send() 和 res.json() 视为返回响应的方式,并且我不确定如何利用它来将图像作为响应返回给调用者。
如果我喜欢这样
res.send(absolute_path_to_the_file)
它只会向我发送目录路径的字符串。我期望的是从目录路径表示的目录发送的图像。
为此需要帮助。
在这里为那些也想知道的人回答我自己的问题..
我在 NextJS 中发布了一个关于它的帖子,他们给了我一个很好的答案 - 这里
有 2 种方法可以使用 readStream
var filePath = path.join(__dirname, 'myfile.mp3');
var stat = fileSystem.statSync(filePath);
response.writeHead(200, {
'Content-Type': 'audio/mpeg',
'Content-Length': stat.size
});
var readStream = fileSystem.createReadStream(filePath);
// We replaced all the event handlers with a simple call to readStream.pipe()
readStream.pipe(response);
或者将对象更改为缓冲区并使用发送方法
/*
Project structure:
.
├── images_folder
│ └── next.jpg
├── package.json
├── pages
│ ├── api
│ │ └── image.js
│ └── index.js
├── README.md
└── yarn.lock
*/
// pages/api/image.js
import fs from 'fs'
import path from 'path'
const filePath = path.resolve('.', 'images_folder/next.jpg')
const imageBuffer = fs.readFileSync(filePath)
export default function(req, res) {
res.setHeader('Content-Type', 'image/jpg')
res.send(imageBuffer)
}
这两个答案都适用于我的情况。使用
process.cwd()
导航到需要作为响应发送的文件/图像。
根据NextJS 13,当使用App Router时,您可以按照以下方式进行:
// before
res.status(200).setHeader('content-type', 'image/png').send(data);
// after
return new Response(data, { headers: { 'content-type': 'image/png' } });
// or
const response = new NextResponse(data)
response.headers.set('content-type', 'image/png');
return response;
注意:
data
可以是Blob/Stream/Buffer。
有关更多信息,请参阅此 GitHub Issue。
这是一个真实的示例,更高级,使用 Sentry 进行调试,并返回具有动态 CSV 文件名的流。 (和 TypeScript 类型)
它可能不如其他答案那么有帮助(由于其复杂性),但有一个更完整的现实世界示例可能会很有趣。
请注意,我对流并不熟悉,我所做的并不是100%最有效的方法,但它确实有效。
src/pages/api/webhooks/downloadCSV.ts
import { logEvent } from '@/modules/core/amplitude/amplitudeServerClient';
import {
AMPLITUDE_API_ENDPOINTS,
AMPLITUDE_EVENTS,
} from '@/modules/core/amplitude/events';
import { createLogger } from '@/modules/core/logging/logger';
import { ALERT_TYPES } from '@/modules/core/sentry/config';
import { configureReq } from '@/modules/core/sentry/server';
import { flushSafe } from '@/modules/core/sentry/universal';
import * as Sentry from '@sentry/node';
import {
NextApiRequest,
NextApiResponse,
} from 'next';
import stream, { Readable } from 'stream';
import { promisify } from 'util';
const fileLabel = 'api/webhooks/downloadCSV';
const logger = createLogger({
fileLabel,
});
const pipeline = promisify(stream.pipeline);
type EndpointRequestQuery = {
/**
* Comma-separated CSV string.
*
* Will be converted into an in-memory stream and sent back to the browser so it can be downloaded as an actual CSV file.
*/
csvAsString: string;
/**
* Name of the file to be downloaded.
*
* @example john-doe.csv
*/
downloadAs: string;
};
type EndpointRequest = NextApiRequest & {
query: EndpointRequestQuery;
};
/**
* Reads a CSV string and returns it as a CSV file that can be downloaded.
*
* @param req
* @param res
*
* @method GET
*
* @example https://753f-80-215-115-17.ngrok.io/api/webhooks/downloadCSV?downloadAs=bulk-orders-for-student-ambroise-dhenain-27.csv&csvAsString=beneficiary_name%2Ciban%2Camount%2Ccurrency%2Creference%0AAmbroise%20Dhenain%2CFR76%204061%208802%208600%200404%208805%20373%2C400%2CEUR%2CBooster%20Unly%20%20septembre%0AAmbroise%20Dhenain%2CFR76%204061%208802%208600%200404%208805%20373%2C400%2CEUR%2CBooster%20Unly%20%20octobre%0AAmbroise%20Dhenain%2CFR76%204061%208802%208600%200404%208805%20373%2C400%2CEUR%2CBooster%20Unly%20%20novembre%0A
*/
export const downloadCSV = async (req: EndpointRequest, res: NextApiResponse): Promise<void> => {
try {
configureReq(req, { fileLabel });
const {
csvAsString,
downloadAs = 'data.csv',
} = req?.query as EndpointRequestQuery;
await logEvent(AMPLITUDE_EVENTS.API_INVOKED, null, {
apiEndpoint: AMPLITUDE_API_ENDPOINTS.WEBHOOK_DOWNLOAD_CSV,
});
Sentry.withScope((scope): void => {
scope.setTag('alertType', ALERT_TYPES.WEBHOOK_DOWNLOAD_CSV);
Sentry.captureEvent({
message: `[downloadCSV] Received webhook callback.`,
level: Sentry.Severity.Log,
});
});
await flushSafe();
res.setHeader('Content-Type', 'application/csv');
res.setHeader('Content-Disposition', `attachment; filename=${downloadAs}`);
res.status(200);
await pipeline(Readable.from(new Buffer(csvAsString)), res);
} catch (e) {
Sentry.captureException(e);
logger.error(e.message);
await flushSafe();
res.status(500);
res.end();
}
};
export default downloadCSV;
代码基于 Next Right Now 样板,如果您想深入了解配置(Sentry 等):https://github.com/UnlyEd/next-right-now/blob/v2-mst -aptd-at-lcz-sty/src/pages/api/webhooks/deploymentCompleted.ts
除了 Fred A 的回答之外,如果您的代码遇到困难,这是最后一行。我通过他的回答中的链接找到了如何解决这个问题。我写了一条回复,但为了更好的可见性,我将完整的答案放在这里。
import fs from "fs";
export default function(req, res) {
const filePath = path.join(__dirname, 'myfile.mp3');
const { size } = fs.statSync(filePath);
res.writeHead(200, {
'Content-Type': 'audio/mpeg',
'Content-Length': size,
});
const readStream = fs.createReadStream(filePath);
await new Promise(function (resolve) {
readStream.pipe(res);
readStream.on("end", resolve);
});
});
假设您有
byteArray
形式的文件,那么您可以在 Next.js API 中执行此操作以将文件发送到浏览器:
res.setHeader('Content-Disposition', 'attachment; filename="filename.file-extension"');
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Content-Length', byteArray.length);
res.status(200).send(Buffer.from(byteArray));