【发布时间】:2021-03-02 17:04:08
【问题描述】:
我在 nodejs 后端创建 pdf 并将它们作为 blob 存储在我的数据库中。
在我的前端(React)中,我正在调用 api 并接收整个 blob 数据列表并将其转换为如下文件。然后从它们创建 URL 并将 URL 存储为数组和状态。
componentDidMount() {
const fileURLs = [];
fetch(`/api/invoices`)
.then((res) => res.json())
.then((invoices) => {
this.setState({ invoices });
invoices.map((inv) => {
const file = new Blob([inv.file_content], {
type: "application/pdf",
});
const fileURL = URL.createObjectURL(file);
fileURLs.push(fileURL);
});
this.setState({ fileURLs });
})
}
我收到的 blob 数据也如下所示: blob data
在我的 render() 中,我将 url 链接到锚标记以在新选项卡中打开它。网址看起来像这样:blob:http://localhost:3000/1b5c4fe3-bb7c-4850-8804-d850f824b07f
this.state.invoices.map((p, i) => (
...
<a
href={this.state.fileURLs[i]}
target="_blank"
rel="noopener noreferrer"
>
{p.file_name}
</a>
...
)
如果我访问该网址,它只会显示 无法加载 PDF 文档。
在我的后端,我将 response.arrayBuffer() 存储为我的 blob 数据。
我还将 pdf 保存在本地文件系统中只是为了测试,它可以毫无问题地创建 pdf 文件。
编辑:我也附上了我的一些后端代码。
.then(async data => {
const response = await fetch(api, {
method: "GET",
headers: {
"Accept": "application/octet-stream",
"Authorization": "Bearer " + token
},
encoding: null
});
const filename = response.headers.get('Content-Disposition')
.split(';')
.find(n => n.includes('filename='))
.replace('filename=', '')
.trim();
const invoice = await response.arrayBuffer();
return [filename, invoice];
})
.then(data => {
const created = moment().format('YYYY-MM-DD HH:mm:ss');
//save pdf file in local directory
var base64Str = Buffer.from(data[1]).toString('base64');
base64.base64Decode(base64Str, './frontend/src/invoices/' + data[0]);
// save pdf in database table
const query = `call create_invoice_procedure('${data[0]}','${data[1]}','${created}')`;
connection.query(query, (err, rows) => {
if (err) {
throw err;
}
});
})
获取所有发票的发票路径:
router.get('/', (req, res) => {
var query = `SELECT * from table`;
connection.query(query, (error, results) => {
if (error) {
return error;
}
else {
res.json(results);
}
})
});
所以我的问题是我无法在前端查看或下载 pdf。
【问题讨论】:
-
将整个 PDF 存储在应用程序状态中是否有特定原因?为什么不让节点提供一个端点来提供 PDF 并在您的前端链接到该端点? (另外,控制台输出显示包含 17 个字节的缓冲区,这听起来像是您不小心发送了文件名,而不是二进制数据?)
-
1.所以你是说我应该在后端创建blob url并将其保存在数据库中的一个字段中并在前端进行相应的访问?我也会试试这个。 2. 在后端,我发送字节长度约为 44000 的数组缓冲区,但在前端它肯定很小,即使它是相同的字段。
-
不,忘记创建 blob url。我是说“编写节点代码,以便转到
localhost:3000/api/invoices/:filename使节点为文件服务”。此外,如果您的前端收到 17 个字节,那么您的后端可能正在提供 17 个字节,抱歉。最后,您的后端数据库不应该存储二进制 PDF 的 Blob,它应该存储每张发票的数据,以便动态构建 PDF。如果您需要某种数字存档,您可能应该改用服务器的文件系统。 -
您存储在 db 中的数据实际上是数组缓冲区。 nodejs 本身不支持 Blob。我在我的应用程序中为使其工作所做的工作完全相同,获取数组缓冲区(通过从 FS 动态读取 pdf)-> 在前端将其转换为 blob -> 从 blob 创建 objectUrl 并使用 window.open带有一些附加参数。这就是我怀疑您可能因为锚标签而遇到此问题的原因。另外,您使用的数据库是什么?它可能有更好的方法来存储 pdf 文件,例如 mongodb 中的 gridFs
-
@ChrisG 你的建议很有希望。我将数据存储为 json 并在需要的地方使用数据构建 pdf。谢谢,我会告诉你的。
标签: javascript node.js reactjs pdf blob