使用 NextJS API 发送文件作为响应
Posted
技术标签:
【中文标题】使用 NextJS API 发送文件作为响应【英文标题】:Send file as response using NextJS API 【发布时间】:2020-11-13 22:20:31 【问题描述】:如题,
在 NodeJs + Express 中,我可以使用以下行返回一个文件作为响应
res.sendFile(absolute_path_to_the_file)
如果我想从 NextJs 目录中的输出文件夹返回单个图像,我如何使用 NextJs API 实现这一点?我只能将 res.send() 和 res.json() 视为返回响应的方式,我不确定如何利用它来将图像作为响应返回给调用者。
如果我喜欢这个
res.send(absolute_path_to_the_file)
它只会向我发送目录路径的字符串。我期望的是从目录路径表示的目录发送的图像。
在此需要帮助。
【问题讨论】:
aw 它没有回答...我被困在同一件事上,你有运气吗? @rakeshshrestha 我在 Vercel Github 上问了同样的问题,他们用这个回答了我 - github.com/vercel/next.js/discussions/… .. 尚未测试,但给出的答案看起来不错 【参考方案1】:在这里为那些也想知道的人回答我自己的问题..
我在 NextJS 中发了一个帖子,他们给了我一个很好的答案 - here
两种方法是使用 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);
或者把对象改成buffer,使用send方法
/*
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()
导航到需要作为响应发送的文件/图像。
【讨论】:
如果要更改下载的文件名res.setHeader('Content-disposition', 'attachment; filename=filename.ext');
,可以设置Content-disposition
标题【参考方案2】:
这是一个真实世界的示例,它更高级,使用 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;
如果您想深入了解配置(Sentry 等),代码基于 Next Right Now 样板:https://github.com/UnlyEd/next-right-now/blob/v2-mst-aptd-at-lcz-sty/src/pages/api/webhooks/deploymentCompleted.ts
【讨论】:
以上是关于使用 NextJS API 发送文件作为响应的主要内容,如果未能解决你的问题,请参考以下文章