Is it possible to directly parse a multipart/mixed response without needing to first convert it into a string?

My current challenge involves receiving a multipart/mixed response over HTTP that includes JSON data and PDFs in byte format. Due to Angular's limitations with handling such responses, I have resorted to converting the response into a string using the responseType: 'text' option.

To work with this data, I break down the response, extract the JSON, and transform the PDF data into a Blob as shown below:

let pdf: Blob = new Blob([new TextEncoder().encode(bytestring)], { type: 'application/pdf' });

The issue arises when attempting to create a download link for the PDF using window.URL.createObjectURL(pdf), resulting in a damaged file that cannot be opened.

I have verified that Angular utilizes UTF-8 encoding when turning the response into a string. Additionally, setting up a separate route allows me to request a single PDF independently with responseType: 'blob' which successfully downloads a functional PDF. Comparison between the original PDF and the damaged one at a byte level in VS Code reveals no noticeable differences.

Given my ability to transfer a working PDF separately but facing challenges within the multipart request, it appears that the conversion of a PDF to a string and back may be causing issues. Is there a solution that avoids transforming the PDF into a string?

Answer №1

After much exploration, I've cracked the code. The key lies in using responseType: 'blob' for the entire response and then converting it into both text and bytes. This allows you to parse JSON data, PDF headers, and build PDF files efficiently. Below is my functional Typescript implementation.

public async processMultipartData(multipartBody: Blob): Promise<MyMultipartResponse> {

    let bodyData: Uint8Array = new Uint8Array(await multipartBody.arrayBuffer());
    let bodyText: string = await multipartBody.text();
    
    // Extract file names from Content-Disposition Header.
    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];
    // Parse the extracted JSON data into a JavaScript object.
    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()!;

        // Store binary data, filename, and download link in a custom class.
        pdfFiles.push(new MyPDFFile(filename.substring(filename.indexOf('"') + 1), pdfData, window.URL.createObjectURL(pdfData)));
        foundStart = false;             
    }

    return new MyMultipartResponse(responseData, pdfFiles);
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The type '{}' cannot be assigned to type 'IntrinsicAttributes & FieldsProp'. This error message is unclear and difficult to understand

"The error message "Type '{}' is not assignable to type 'IntrinsicAttributes & FieldsProp'.ts(2322)" is difficult to understand. When I encountered this typeerror" import { useState } from "react"; import { Card } fr ...

There are no HTTP methods available in the specified file path. Make sure to export a distinct named export for each HTTP method

Every time I attempt to run any code, I encounter the following error message: No HTTP methods exported in 'file path'. Export a named export for each HTTP method. Below is the content of my route.ts file: import type { NextApiRequest, NextApi ...

Utilizing the reduce() function to simultaneously assign values to two variables from data input

Looking to simplify the following function using reduce(), as the operations for variables selectedEnrolled and selectedNotEnrolled are quite similar. I attempted to use map(), but since I wasn't returning anything, it led to unintended side effects ...

Set Chartjs2 data to display as a doughnut chart starting from 0

When working with a doughnut chart and having data values set to 0, it is important to ensure that the default chart still displays with equal size parts. To achieve this, one can utilize the beginAtZero option in JavaScript: JS scales: { yAxes: [{ ...

Improving JavaScript Functions: Minimize duplication of helper methods

I have a set of helper functions that check for the presence of specific strings in an array and certain steps before triggering other functions. The reason for keeping them separated is because arrTours must be associated with only those arrSteps. // Help ...

Creating a signature for a function that can accept multiple parameter types in TypeScript

I am facing a dilemma with the following code snippet: const func1 = (state: Interface1){ //some code } const func2 = (state: Interface2){ //some other code } const func3: (state: Interface1|Interface2){ //some other code } However, ...

Is it possible to conceal dom elements within an ng-template?

Utilizing ng-bootstrap, I am creating a Popover with HTML and bindings. However, the ng-template keeps getting recreated every time I click the button, causing a delay in the initialization of my component. Is there a way to hide the ng-template instead? ...

Angular design pattern: organizing rows according to the month

I currently possess the following items: { "pk": 33, "name": "EVENT 634", "start_date": "2022-05-11T00:00:00", "end_date": "2022-05-14T00:00:00" }, { "pk": ...

Chaining switchMaps(s) consecutively

I am currently in the process of developing an editor that will allow users to select 1 account from a list of accounts, also known as a Chart of Accounts (COA). The author will have the ability to specify the type/subtype that should be selected, and the ...

Issue: Unable to find solutions for all parameters in NoteService: (?)

After following a tutorial on Angular 2 from , I encountered the mentioned error when running my API. The browser indicates that there are unresolved parameters in the following service: import {Injectable} from '@angular/core'; import { ApiSe ...

Deduce the generic types of conditional return based on object property

My goal is to determine the generic type of Model for each property. Currently, everything is displaying as unknown[] instead of the desired types outlined in the comments below. playground class Model<T> { x?: T } type ArgumentType<T> = T ...

When trying to update a form field, the result may be an

Below is the code for my component: this.participantForm = this.fb.group({ occupation: [null], consent : new FormGroup({ consentBy: new FormControl(''), consentDate: new FormControl(new Date()) }) }) This is th ...

Steps for inserting an additional header in an Angular table

https://i.stack.imgur.com/6JI4p.png I am looking to insert an additional column above the existing ones with a colspan of 4, and it needs to remain fixed like a header column. Here is the code snippet: <div class="example-container mat-elevation-z8"> ...

Determine an expression based on a string in Typescript

Looking at the code snippet below, everything appears to be in order (view playground): type PathParam<T> = T extends `${string}:${infer U}` ? U : never; type Param = PathParam<"/post/:post_id">; // type Param = "post_id" ...

Storing checkbox status in Angular 7 with local storage

I am looking for a way to keep checkboxes checked even after the page is refreshed. My current approach involves storing the checked values in local storage, but I am unsure of how to maintain the checkbox status in angular 7 .html <div *ngFor="let i ...

Having trouble installing the Angular/CLI on macOS Mojave due to a node-pre-gyp error?

I recently formatted my iMac and deleted all files on the hard drive. However, upon installing Angular CLI 7, I encountered an error log message in the terminal console. Environment macOS: Mojave 10.14.2 node: v10.15 npm: 6.4.1 Console Error miguels- ...

Ensuring Type Compatibility Between Classes and Object Literals in TypeScript

When working with TypeScript, it is important to note that an object literal can be assigned to a class typed variable as long as the object provides all properties and methods required by the class. class MyClass { a: number; b: string; } // The co ...

There are no specifications available for the project that is located in the same workspace as the main

I have been given the responsibility of testing an application for a company. When I execute the main project using "ng test", it runs smoothly and detects all its test suites. However, when I attempt to run another project within the same workspace named ...

Retrieve information from various MongoDB collections

Greetings! I currently have a database with the following collections: db={ "category": [ { "_id": 1, "item": "Cat A", }, { "_id": 2, "item": "Cat B" ...

In Angular, you can easily modify and refresh an array item that is sourced from a JSON file by following these steps

Currently, I am working on implementing an edit functionality that will update the object by adding new data and deleting the old data upon updating. Specifically, I am focusing on editing and updating only the comments$. Although I am able to retrieve th ...