Click here to Skip to main content
15,880,725 members
Articles / Programming Languages / Javascript

Download File via Ajax

Rate me:
Please Sign up or sign in to vote.
4.75/5 (4 votes)
25 Jul 2021CPOL3 min read 22.8K   2   4
How to retrieve file from web page that uses JavaScript for its interactions
In this article, we’re going to look at how to retrieve the file from a web page that uses JavaScript for its interactions (in my case, what inspired this was a React.js application).

In a previous post, we discussed how to create a zip file on demand on the server so that it can be served via Web API. The code for the previous post and this one for can be found in my github repository.

So today, we’re going to look at how to retrieve the file from a web page that uses JavaScript for its interactions (in my case, what inspired this was a React.js application).

First, we’re going to cover why we need this workaround and secondly, what solution I ended up using by doing some research on StackOverflow, as well as MDN to figure out how and why it works.

To test this out, we’re going to create an empty HMTL page with a button on it. Next, we’re going to hook that button up with an event for when it’s clicked. The HTML will look something like this:

XML
<button type="button" id="testButton" onclick="getArchiveAjax()">Get Archives</button>

Next up, I added a script tag for Axios since I find it very easy to use especially for this scenario, though since the issue is browser related, you could use any framework/library you wish that allows you to implement this workaround.

So to add Axios without too much fuss for this case, I added the following script tag in the head of the page:

XML
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

So now that the infrastructure is in place, we can go on and start implementing the getArchiveAjax function. We will start simple just so that we can also learn what the issue is.

JavaScript
function getArchiveAjax(){
    axios.get("/ServerSideZip/getArchive").then(x=> {
        console.log(x)
    });
}

When we run this function by clicking the button, we will see that the response that comes back, contains the bytes of the file in Base64 format. In theory, we could write it to disk like that, but JavaScript in the browser doesn’t let us do that due to security concerns because we could end up with all sorts of malware from websites.

Because of these security concerns, browsers only let you download files from a user interaction, or a simulated used interaction.

For this, we need to create a link on the page so that either the user can click it, or, in our case, the script can click the link. So we’re going to look into how to create a URL out of a byte array by using the static URL functionality form JavaScript.

As we can see from the documentation, the URL.createObjectURL() function requires as an input a blob. Luckily for us, we can instruct Axios to request the file as a blob using the following configuration:

JavaScript
function getArchiveAjax(){
    axios.get("/ServerSideZip/getArchive",{
        responseType: "blob"
    }).then(x=> {
        console.log(x);
    });
}

With this now, we will receive the data as a Blob and as such, we could create the URL from it using the URL.createObjectURL() function which stores the file in memory and provides an unique URL towards that file.

JavaScript
function getArchiveAjax(){
    axios.get("/ServerSideZip/getArchive",{
        responseType: "blob"
    }).then(x=> {
        const url = URL.createObjectURL(x.data); 
        console.log(url);
        URL.revokeObjectURL(url);
    });
}

From the documentation, we also learned that it is a good practice to revoke the URL so that we release the used up memory.

Next up, we need to create the hyperlink for the file so that it can be clicked, first, we create the anchor element dynamically, provide it the URL, attach it to the DOM, click it, and then we remove the element so that we clean up after ourselves. The code looks as follows:

JavaScript
function getArchiveAjax(){
    axios.get("/ServerSideZip/getArchive",{
        responseType: "blob"
    }).then(x=> {
        const url = URL.createObjectURL(x.data); 
        const link = document.createElement('a');
        link.href = url;
        document.body.append(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);
    });
}

When we run this, we will see that the save file dialog shows up (I’m using Firefox), but something is odd about the file name.

Image 1

This is easy to file by adding an additional attribute to the generated hyperlink:

JavaScript
function getArchiveAjax(){
    axios.get("/ServerSideZip/getArchive",{
        responseType: "blob"
    }).then(x=> {
        const url = URL.createObjectURL(x.data); 
        const link = document.createElement('a');
        link.href = url;
        link.setAttribute('download', "TestFileNameClient.zip");
        document.body.append(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);
    });
}

Now when we get the file dialog, we will see it with the name we gave it:

Image 2

But we can do one better, because the server might actually send file name associated with that zip file, which we can extract from the response header with a little string manipulation which gets us to our final form of the getArchiveAjax function:

JavaScript
function getArchiveAjax(){
    axios.get("/ServerSideZip/getArchive",{
        responseType: "blob"
    }).then(x=> {
        const filenameHeader = x.headers["content-disposition"].split(';')[1];
        const filename = filenameHeader.substring(filenameHeader.indexOf("=")+1);
        const url = URL.createObjectURL(x.data); 
        const link = document.createElement('a');
        link.href = url;
        link.setAttribute('download', filename);
        document.body.append(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);
    });
}

This is the way I ended up using the functionality for downloading a file via a Web API call and saving it on disk.

I hope you enjoyed the post and that it helped you.

Thank you and see you next time.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Romania Romania
When asked, I always see myself as a .Net Developer because of my affinity for the Microsoft platform, though I do pride myself by constantly learning new languages, paradigms, methodologies, and topics. I try to learn as much as I can from a wide breadth of topics from automation to mobile platforms, from gaming technologies to application security.

If there is one thing I wish to impart, that that is this "Always respect your craft, your tests and your QA"

Comments and Discussions

 
QuestionUrl to axios.get Pin
Member 101098008-Aug-21 19:43
Member 101098008-Aug-21 19:43 
AnswerRe: Url to axios.get Pin
Vlad Neculai Vizitiu12-Aug-21 8:30
Vlad Neculai Vizitiu12-Aug-21 8:30 
GeneralRe: Url to axios.get Pin
Member 1010980012-Aug-21 12:15
Member 1010980012-Aug-21 12:15 
GeneralRe: Url to axios.get Pin
Vlad Neculai Vizitiu12-Aug-21 20:34
Vlad Neculai Vizitiu12-Aug-21 20:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.