Tips for utilizing the download stream with StreamSaver.js in Axios?

Within my server-side application, constructed using the Spring Boot framework, I have implemented a method that returns a stream resembling the following:

public ResponseEntity<StreamingResponseBody> downloadFiles(@RequestBody DownloadRequest payload) {

    // Defining proper header
    String contentDisposition = "attachment;filename=download.zip";

    // Constructing the response stream
    StreamingResponseBody stream = outputStream -> {
        archiveManagerService.downloadFiles(payload.getArchiveId(), payload.getFiles(), outputStream);
    };

    return ResponseEntity.ok()
            .contentType(MediaType.parseMediaType("application/zip"))
            .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition)
            .body(stream);
}

This setup functions correctly as intended, enabling file downloads through Postman. Now, the next step involves invoking this endpoint from the client-side leveraging Axios. Upon research, a library named StreamSaver.js came to light. The library seamlessly integrates with fetch (check source for example code). Yet, integrating it with Axios proves slightly challenging.

The current implementation within my Vuejs-driven code appears as follows:

import axios from 'axios';
import streamSaver from 'streamsaver';

const instance = axios.create({
    baseURL: '<my_base_url>',
    headers: {
        'Content-Type': 'application/json'
    }
});

instance.post('/download', postData, {
    responseType: 'stream'
})
.then(response => {
    // How should I proceed here? The code snippet below seems ineffective
    const fileStream = streamSaver.createWriteStream('download.zip');
    response.data.pipe(fileStream);
});

A runtime error prompts stating

response.data.pipe is not a function

Seeking guidance on how to effectively consume the stream via Axios on the client-side or exploring any alternate solutions are greatly appreciated.

Answer №1

It has been highlighted by schnaidar that currently, Axios is unable to handle streams from the client-side (issue 479).

As a workaround, utilizing the fetch API is suggested. Nonetheless, this method is experimental and may not be compatible with all browsers. Based on testing, it performs well in Google Chrome but encounters issues in Firefox and Safari. To address this limitation, I turned to another JavaScript library known as web-streams-polyfill.

Provided below is an abridged version of my code:

import { WritableStream } from 'web-streams-polyfill/ponyfill';
import streamSaver from 'streamsaver';

fetch(url, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
})
.then(response => {

    let contentDisposition = response.headers.get('Content-Disposition');
    let fileName = contentDisposition.substring(contentDisposition.lastIndexOf('=') + 1);

    // The following code snippet has been adapted from a StreamSaver.js example
    // https://jimmywarting.github.io/StreamSaver.js/examples/fetch.html

    // In case the WritableStream is unavailable (in Firefox, Safari), utilize the ponyfill
    if (!window.WritableStream) {
        streamSaver.WritableStream = WritableStream;
        window.WritableStream = WritableStream;
    }

    const fileStream = streamSaver.createWriteStream(fileName);
    const readableStream = response.body;

    // Enhanced optimization
    if (readableStream.pipeTo) {
        return readableStream.pipeTo(fileStream);
    }

    window.writer = fileStream.getWriter();

    const reader = response.body.getReader();
    const pump = () => reader.read()
        .then(res => res.done
            ? writer.close()
            : writer.write(res.value).then(pump));

    pump();
})
.catch(error => {
    console.log(error);
});;

The approach involves verifying the availability of window.WritableStream within the current browser environment. If not present, directly assign the WritableStream from ponyfill to the streamSaver.WritableStream property.

This setup was tested across Google Chrome 78, Firefox 70, Safari 13; with web-streams-polyfill 2.0.5, and StreamSaver.js 2.0.3

Answer №2

It appears that the browser does not currently support streaming functionality for the specific task, as mentioned in more detail at https://github.com/axios/axios/issues/479. In order to proceed, utilizing fetch similar to the given example may be necessary.

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

Moving various divisions through Javascript by clicking various buttons although sharing the same identifier

I am working with the script below: $(document).ready(function() { $('.expandButton').click(function() { $('.expandableSection').toggle("slide"); }); }); My goal is to apply this script to multiple sections. However, ...

Trouble with the Ngx-Captcha feature

I am currently utilizing https://www.npmjs.com/package/ngx-captcha/v/11.0.0. <ngx-recaptcha2 #captchaElem [siteKey]="'6Leh1ZIjAAAAAG8g0BuncTRT-VMjh3Y7HblZ9XSZ'" (success)="handleSuccess($event)" [useGlobalDomain]="fals ...

Looking for the child route parameter in Ember - A comprehensive guide

Consider having 2 routes: /products - displays a list of products -/:id - displays details of a specific product When a URL is provided for the above routes, the /products route must be able to access the /:id parameter in order to highlight that prod ...

Horizontal Accordion Design for Cascading Style Sheets (CSS) Books

I am currently working on developing a page that features a book layout. This page will include tabs that users can expand individually. If you would like to see a working example, you can check out this link: https://codesandbox.io/s/book-layout-l28gh?fi ...

After changing the form action using jQuery, the GET variables mysteriously vanish

I am attempting to modify the action URL of a form using jQuery. Below is the code I have implemented: <form id='form_a' action='browse.php' method='get'> <input type="submit" value="Filter" id='but_a'& ...

Converting JavaScript to PHP and encountering problems with POST encoding

Trying to send a string from JavaScript on the client-side to a PHP script on the back-end. The string has special quotes like ’ and “. When checking the console in Chrome, the POST headers show these special characters as they are. On the PHP side, I ...

Change the time format from '18/10/2016 10:31:22PM' to '18/10/2016 22:31:22' in JavaScript

var currentDate = new Date('18/10/2016 10:31:22PM'); var formattedTime = currentDate.toLocaleTimeString(); alert(formattedTime); This code is returning an 'invalid date' output. To fix this issue, I need to convert the date format to ...

The functionality of a jQuery click event may cease to work once a loader has been incorporated into a

Since implementing a loading feature in my Vue template, I have encountered issues with jQuery not functioning properly: var element = document.getElementById('vue-project') if (element !== null) { var getData = await axios.get('./dat ...

The jQuery function is running double even after I cleared the DOM with the empty() method

Twice, or multiple times if going back and forth, the Jquery function is triggered. Upon loading LoginMenu.jsp, WorkOrder.jsp is loaded within a specified ID. Once WorkOrder.jsp loads, it then loads schedule.jsp in the schedule tab defined in WorkOrders.j ...

What is the best way to manage numerous asynchronous post requests in AngularJS?

$scope.savekbentry = function (value) { console.log('save clicked'); console.log(value); console.log($scope.kbentry.kbname); $scope.kbentry.mode = value; var kbname = $scope.kbentry.kbname; var kbd ...

What is the best way to implement automatic scrolling to the bottom of a materialUI drawer in React after a page refresh?

I am facing an issue with my Material UI Drawer, which contains numerous checkboxes and radio buttons for an advanced search page. Whenever a checkbox or radio button is clicked, it triggers an API request to fetch results instantly without requiring a sub ...

Animating images with Jquery

As a beginner in Javascript and Jquery, I am currently learning about image animation. However, I have encountered a question regarding moving an image from bottom left to top right within the window. Is there a more efficient way to achieve this compared ...

Displaying a custom error page in Next.js with the appropriate status code

I recently implemented a custom error page following the instructions provided in the documentation. My goal was to use this error page for specific errors that may occur during the getStaticProps function. Here's how I structured it: const Page: Next ...

How do I use jQuery to make a div appear when the previous one closes?

I've been experimenting with some code and it's gotten pretty lengthy. It works fine with just a few divs, but what if I need to implement this on 20 or more...? Is there a way to condense the code I've written? (I'm still new to jque ...

What is the process for testing and executing the code I've written on ACE Cloud 9?

I have integrated ACE on my website to enable users to code freely. My question is, how can I execute the Python code they write? I want to add a run button in the bottom left corner (which I can style using CSS), but I'm not sure how to actually run ...

Displaying unique input values with ng-model

Within the controller, there is a variable that monitors the page index (starting at 0) for a paginated table: var page { pageNumber: 0; } Query: How can I display this pageNumber variable in the HTML, but always incremented by +1? (since the index=0 p ...

Accessing the database variable in controller files with Node.js

I am new to node.js and currently using lowdb for my database as I start building my app. In the index.js file, I have set up my Express server along with routes: var express = require('express'); var app = express(); var bodyParser = require(& ...

What steps should be taken to resolve the error message "This Expression is not constructable"?

I'm trying to import a JavaScript class into TypeScript, but I keep getting the error message This expression is not constructable.. The TypeScript compiler also indicates that A does not have a constructor signature. Can anyone help me figure out how ...

Utilize VueJS to upload and visualize a file input on your website

I am currently working with TypeScript and Haml in conjunction with vue.js. My goal is to enable users to upload and view a file seamlessly using the vue.js framework. I have successfully managed to upload an image, however, I am facing an issue where the ...

Issue importing color palettes from JSON into Tailwind CSS

While working on my Vue.js project with Tailwind CSS, I ran into an issue. It seems that Tailwind is not picking up the custom color classes specified in my JSON configuration for 'primary', 'secondary', and 'gray'. Instead of ...