我从组件获取 HTML 并将其发送到将生成 PDF 的端点。之后我想下载生成的 PDF。
这是我用来调用端点并在正文中发送
html
的代码:
const formDataHtml = formData.get('html') as string;
const html = new URLSearchParams({ html: formDataHtml });
const response = await fetchApi(`/pdf/html2pdf`, {
request,
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: html,
shouldParseResponse: false,
shouldStringifyBody: false,
});
if (!response.success) {
return json({ error: response.error.message }, response.error.statusCode);
}
const pdfFileData = response.data as PdfFileData;
const pdfBuffer = await new Response(pdfFileData.body).arrayBuffer();
const pdfUint8Array = new Uint8Array(pdfBuffer);
return new Response(pdfUint8Array, {
headers: {
'Content-Type': 'application/pdf',
'Content-Disposition': `attachment; filename=protokol.pdf`,
},
});
这是触发下载的“下载 PDF”按钮:
function DownloadPDFForm({endpoint, html, children}) {
const fetcher = useFetcher<any>();
const downloadRef = React.useRef<HTMLAnchorElement>(null);
const args = { html };
React.useEffect(() => {
const response = fetcher.data;
if (!response) return;
const blob = new Blob([response], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'file.pdf';
a.click();
URL.revokeObjectURL(url);
}, [fetcher.data]);
function onClick() {
const formData = new FormData();
formData.set('endpoint', endpoint);
formData.set('intent', 'download-pdf');
for (const [key, value] of Object.entries(args)) formData.append(key, value);
fetcher.submit(formData, {
action: '/api/manage-files',
method: 'POST',
});
}
return (
<div className={cn('flex items-center')}>
<a className="sr-only" ref={downloadRef} href={fetcher.data?.url}>
DOWNLOAD
</a>
<button type="button" onClick={onClick}>DOWNLOAD PDF</button>
</div>
);
}
调用端点时,触发PDF下载,但所有页面都是空白。 formDataHtml 越大,下载的 PDF 中的页面就越多,尽管所有页面都是空白的。
注意:
formDataHtml
、html
不为空,因为我对它们进行了 console.log 记录(还使用一些基本的 HTML 进行了测试)
我必须深入挖掘才能找到问题所在,显然是与 Remix 服务器相关的问题。我的解决方法是解码从服务器返回的 Base64 字符串:
const base64ToBuffer = (base64: string): ArrayBuffer => {
const binaryString = window.atob(base64); // Decode base64 string
const length = binaryString.length;
const bytes = new Uint8Array(new ArrayBuffer(length));
for (let i = 0; i < length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
};