我通过 HTTP 收到
multipart/mixed
响应,其中包含一些 JSON 数据以及字节格式的 PDF。由于 Angular 无法简单地处理此类响应,因此我当前的方法是使用 responseType: 'text'
选项将响应转换为字符串。
然后,我将响应分开,解析 JSON,并将 PDF 数据放入一个 Blob 中,如下所示:
let pdf: Blob = new Blob([new TextEncoder().encode(bytestring)], { type: 'application/pdf' });
但是,当我想用
window.URL.createObjectURL(pdf)
为PDF创建下载链接时,下载的PDF已损坏并且无法打开。
我已经确认,当 Angular 将响应转换为字符串时,它使用 UTF-8 编码。我还实现了一个单独的路线,这样我就可以单独请求一个 PDF,从而允许我使用
responseType: 'blob'
,它可以工作并下载功能正常的 PDF。此外,我强制 VS Code 打开原始 PDF 文件和损坏的 PDF 文件,并且字节表示是相同的。
由于我能够在不将 PDF 作为多部分请求的一部分发送时传输它,因此在我看来,损坏 PDF 的唯一可能原因是我解析多部分请求的方式,并且我在其他地方读过将 PDF 转换为字符串然后再转换回 PDF 可能会出现问题。那么,有什么办法可以做到这一点而不将其转换为字符串吗?
我已经找到解决办法了。诀窍是对整个响应使用
responseType: 'blob'
,然后将整个内容转换为文本和字节。然后,您可以使用文本表示来解析 JSON 数据以及 PDF 标头和字节表示来构建 PDF 文件本身。下面是我的工作 Typescript 代码。
public async parseMultipartResponse(multipartBody: Blob): Promise<MyMultipartResponse> {
let bodyData: Uint8Array = new Uint8Array(await multipartBody.arrayBuffer());
let bodyText: string = await multipartBody.text();
// From the Content-Disposition Header of each file.
let filenames: RegExpMatchArray = bodyText.match(/filename.*\.pdf/gi)!;
let boundary: string = bodyText.substring(0, bodyText.indexOf('\n')).trim();
let responseDataJson: string = bodyText.split(boundary)[1];
// Note that this only creates a plain Javascript object, not an actual instance of th class MyJsonRepresentation.
let responseData: MyJsonRepresentation = JSON.parse(responseDataJson.substring(responseDataJson.indexOf('{')));
let encoder: TextEncoder = new TextEncoder();
let startOfFile: Uint8Array = encoder.encode("%PDF");
let endOfFile: Uint8Array = encoder.encode("%%EOF");
let pdfData: Blob;
let filename: string;
let pdfFiles: MyPDFFile[] = [];
let foundStart: Boolean = false;
let filecontentStart: number = 2 * boundary.length + responseDataJson.length;
scan: for(let i = filecontentStart; i < bodyData.length - endOfFile.length; i++) {
if(!foundStart) {
for(let j = 0; j < startOfFile.length; j++) {
if(bodyData[i + j] != startOfFile[j])
continue scan;
}
filecontentStart = i;
foundStart = true;
}
for(let j = 0; j < endOfFile.length; j++) {
if(bodyData[i + j] != endOfFile[j])
continue scan;
}
pdfData = multipartBody.slice(filecontentStart, i + endOfFile.length, 'application/pdf');
filename = filenames.shift()!;
// I've created a class that stores the binary data together with the filename and a download link.
pdfFiles.push(new MyPDFFile(filename.substring(filename.indexOf('"') + 1), pdfData, window.URL.createObjectURL(pdfData)));
foundStart = false;
}
return new MyMultipartResponse(responseData, pdfFiles);
}