问题描述
我创建了一个 express 应用程序来向客户端提供存储在 Google 云存储中的 PDF 文件
当点击文件的端点时,下载在 Chrome 中间歇性地挂起,下载的 KB 数是随机的,通常在 1000 KB 到 1300 KB 的范围内。但是我通常可以在 Firefox、Safari 和 Postman 中成功下载文件。
我还注意到,如果我省略了强制浏览器尝试呈现 PDF 的“Content-disposition”标头,浏览器会开始在 Chrome 中呈现它,但会以某个随机加载百分比挂起。在 Firefox/Safari 中,它会在第一次加载时以随机百分比挂起,但通常会在立即刷新 URL 时呈现。
我不确定我是否错误地使用了流/缓冲区,或者我是否错误地返回了从 Google Storage 收到的缓冲区。我已经在谷歌上搜索了几个小时,但我的想法已经用完了,如果有人提供任何帮助,我将不胜感激。
是否有更好的方式来提供文件,或者我是否需要以不同于谷歌云存储的方式下载文件?
感谢您看到这里!
// FileService for fetching from Google Storage
export class FilesService {
pdfBucket: Bucket
constructor() {
this.pdfBucket = admin.storage().bucket(config.firebase_ppe_product_pdfs_bucket)
}
getPdfFile(filePath: string) : File {
return this.pdfBucket.file(filePath);
}
async getBufferOfPdfFile(filePath: string) {
const file = await this.getPdfFile(filePath)
// Download the file to a buffer
// Omit the "destination" option from download method to receive a buffer
const [ buffer ] = await file.download()
return buffer
}
}
/* index.ts - main express server */
import * as functions from "firebase-functions";
import logger from "morgan";
import cookieParser from "cookie-parser";
import express from "express";
import cors from "cors";
import files from './routers/files'
const app = express();
app.use(cors());
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use('/files',files);
export const server = functions.https.onRequest(app)
/* files.ts - files router for express server */
import express,{Express} from "express";
import {FilesService} from "../../services/files";
import logger from "../../services/rollbar";
const router = express.Router();
const filesService = new FilesService()
/* GET pdf */
router.get('/product/:slug',async (req,res) => {
const slug = req.params.slug
try {
// Get the file buffer
const data = await filesService.getBufferOfPdfFile(slug)
res.writeHead(200,{
'Content-Type': 'application/pdf','Content-disposition': `attachment; filename=${slug}`,'Content-Length': data.length
})
res.end(data,'binary')
} catch (err) {
res.status(500).send("Looks like there was a problem...")
logger.error(err)
}
});
export default router;
解决方法
终于想通了...似乎我应该直接使用流并将其通过管道传输到 Express 响应中。
这为我修好了
// in the files.ts express handler
// ...
// Get the file stream
const file = await filesService.getPdfFile(`${hostname}/${slug}`)
const stream = file.createReadStream()
res.on('error',err => logger.error(err))
res.set('Content-Type','application/pdf')
if (download) {
res.set('Content-Disposition',`attachment; filename=${slug}`)
}
// Pipe file read stream to the response (write stream)
stream.pipe(res)
stream.on('error',err => logger.error(err))
// ...