我可以解析多部分/混合响应而不必先将其转换为字符串吗?

问题描述 投票:0回答:1

我通过 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 可能会出现问题。那么,有什么办法可以做到这一点而不将其转换为字符串吗?

angular typescript parsing pdf multipart
1个回答
0
投票

我已经找到解决办法了。诀窍是对整个响应使用

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);
}
© www.soinside.com 2019 - 2024. All rights reserved.