I have a react client that takes in user input as a file and sends it to my remote Flask server for storage. I send the file in the form of a Werkzeug FileStorage object and in the remote server I store it with file.save(path). In the react client I'm trying to build a way to download the file from the server, however I'm running into problems. Currently my program is working for downloading .txt files. I'm able to do this though a fetch javascript request:
fetch(FETCH_URL, {
method: 'POST',
body: data,
headers: {
'Content-Type': 'application/json'
}
}).then((response) => {
var a = response.body.getReader();
a.read().then(({ done, value }) => {
saveAsFile(new TextDecoder("utf-8").decode(value), 'filename.txt');
}
);
});
function saveAsFile(text, filename) {
const type = 'application/text'; // modify or get it from response
const blob = new Blob([text], {type});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
}
Thanks to some help I got in this post: Download file in react client from flask remote server
I know this code is specifically made to work only with .txt files based on the type being passed in to Blob, but the front end is not the real problem.
The real problem is in my remote flask server, the following code is what is called in the flask server:
with open(filename, 'r') as f:
contents = f.read()
return contents
I tried returning the file itself but the server gives an error:
"ValueError: I/O operation on closed file."
So I decided to return the contents of the file as shown above.
The problem arises when I try to get a file for example "download.jpeg". Reading the file gives the following error:
"UnicodeDecodeError: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte"
From what I understand Flask works exclusively with 'utf-8' and I assume this means the file in the server is on 'utf-8' encoded.
Does anyone have a suggestion or guidance on a solution or a workaround maybe a way to change the files encoding when I save it on the server or something else that could help me with what I'm trying to do?
Fetch's Response has blob() to convert the response directly to blob, so you don't have to read the stream, you don't have to find out it's content type or anything. Just try the below solution.
fetch(FETCH_URL, {
method: 'POST',
body: data,
headers: {
'Content-Type': 'application/json'
}
}).then((response) => {
response.blob().then((blob) => {
saveBlob(blob, 'filename');
});
});
function saveBlob(blob, filename) {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
}
Try this: make sure to install axios. Also you probably won't have to deal with content type like above said. Obviously changing the method type to POST and bring ur data in.
axios(FETCH_URL, {
method: 'GET',
responseType: 'blob', // important
}).then((response) => { //Creates an <a> tag hyperlink that links the excel sheet Blob object to a url for downloading.
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `${Date.now()}.xlsx`); //set the attribute of the <a> link tag to be downloadable when clicked and name the sheet based on the date and time right now.
document.body.appendChild(link);
link.click(); //programmatically click the link so the user doesn't have to
document.body.removeChild(link);
URL.revokeObjectURL(url); //important for optimization and preventing memory leak even though link element has already been removed.
});
Related
Details ......
Steps:
I created the Axios call for hitting the backend server's request.
I just used responseType: 'blob', and received the response.
Its downloading is fine but causing issues while installing.
its shows there was a problem parsing the package
`axios({
url: 'http://localhost:5000/static/test.apk',
method: 'GET',
responseType: 'blob', // important
}).then((response) => {
const url = window.URL.createObjectURL(response.data));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'test.apk');
document.body.appendChild(link);
link.click();
});`
I want to check, whether I'm doing the correct way to get the.apk file from the server or if there are any other ways to download, If anyone has any ideas, could you help with this
I'm sending data to my Django backend through a POST request, the server then creates a docx using this data and sends it back to me where I'm trying to download it using the following code :
axios.post(route, data).then((res) => {
const filename = "report.docx"
var blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
var link = document.createElement('a');
var URL = window.URL || window.webkitURL;
var downloadUrl = URL.createObjectURL(blob);
link.href = downloadUrl;
link.style = "display: none";
link.download = filename;
document.body.appendChild(link)
link.click()
link.remove()
});
The download of the file works but when I open it, it only contains [Object Object], what am I doing wrong here ? I checked the backend and the document is properly created as it has the right content when I'm saving it when it's generated in the server so it's either that i'm sending it badly or creating the file in the frontend badly.
Here is how I send it from the backend :
#Generating the doc properly before this...
response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document')
report_filename = 'graph-report-'+now.strftime('%Y-%m-%d_%H-%M-%S')+'.docx'
response['Content-Disposition'] = 'attachment; filename=%s'%(report_filename)
document.save(response)
return response
Thanks for helping me.
With Axios, the res in axios.post(route, data).then((res) => is a response object.
The returned data is found in res.data.
Do console.log(res.data) to see that it truly is the binary data you expect.
Do new Blob([res.data], ... to use that data to get the blob. (If it already is a blob courtesy of Axios, you don't necessarily need to bother.)
(As an aside, you don't really need to use fetch() and createObjectURL for this at all since you're forming a download-like binary response; it would be enough to just have the browser POST your desired data e.g. as a form to the endpoint; it would download the response just the same.)
The file that I am generating is being returned in the response as a string instead of prompting the download.
I've seen and tried a couple approaches to it from stackoverflow / other websites and they ultimately all resulted in the exact same problem.
I've tried:
Making a temp file, writing to it, then moving it to a new location with Storage::put
and downloading from that location using response()->download.
Writing to a temp file and echoing/reading it in the streamDownload closure.
Writing to a temp file and reading it after finishing (while setting the Headers beforehand)
I am POSTing a form payload using Axios with the headers
'content-type': 'multipart/form-data',
'X-CSRF-TOKEN': 'csrfToken'
The headers I am using for the response are:
'Content-Type: text/csv',
'Content-Disposition: attachment; filename=myFile.csv'
I've tried setting the headers using header() and by creating an array then passing it to streamDownload/download as header arguments.
The file download works/prompts normally if I simply create a form and submit it with form.submit(). I am only experiencing this problem when I try to do it asynchronously via a direct post request with Axios.
I am at a complete loss for what is causing this discrepancy, since submitting the form normally prompts the download just fine.
I managed to solve this by using the returned response and creating a BLOB with it and prompting the download using the BLOB.
let config = {
headers: {
'content-type': 'multipart/form-data',
'responseType': 'blob',
'X-CSRF-TOKEN': csrfToken
}
};
axios.post(downloadRoute, payload, config).then(response => {
const downloadUrl = window.URL.createObjectURL(new Blob([response.data.fileOutput]));
const link = document.createElement('a');
link.href = downloadUrl;
link.setAttribute('download', 'myfile.csv');
document.body.appendChild(link);
link.click();
link.remove();
});
Hope this helps anyone in the same situation!
I have to call a remote service to download a gzipped file (csv.gz).
I have to use jQuery because I have to set the Authentication HTTP header.
I tried with something like this:
$.ajax({
type: "GET",
url: url
}).done(function (res) {
const a = document.createElement('a');
a.style = 'display: none';
document.body.appendChild(a);
const blob = new Blob([res]);
const url = URL.createObjectURL(blob);
a.href = url;
a.download = _this.attributes.id + '.' + _DEFAULT_DOWNLOADED_FILE_EXTENTION;
a.click();
URL.revokeObjectURL(url);
}).fail(function (err) { });
I'm able to download the file but, after the download, I tried to unzip it on my pc but the file is not correct (corrupted). Probably the Blob creation that I used it's not correct.
You're trying to download binary data, you can fetch it using typed arrays in JavaScript, here's an example in the MDN.
In regards to setting the Authentication header, you don't need jQuery for that. Here's the XHR API that enables you to do it.
I have a backend that I set up to return a file through setting the header
Content-Disposition: attachment;filename=somefile.csv
It works directly in the browser and downloads the file immediately upon invoking the URL that points to that resource.
My goal is to have a button in an Angular 2 template. When the user clicks that button, I'd need to collect some data from the client-side (some IDs) and send it to the server to invoke that file download URL.
I'd like the user to stay on the same page and not have any new tabs open up but simply have the file start downloading (just like when the URL is invoked directly).
It will need to be done through a POST request because I can have quite a bit of data to send to identify what resource needs to be downloaded.
What does the call on the Angular 2 side look like for this? I tried a couple of things but I am obviously on the wrong path.
Any help would be appreciated!
I had a similar issue when i was trying to download a PDF file which my Node server was sending. I was making a GET request on my server with some id details.
This is what worked for me.
Function Calling my service
printBill(receiptID) {
this.BillingService.printBill(receiptID)
.subscribe(res => {
saveAs(res,"InvoiceNo"+receiptID+".pdf");
let fileURL = URL.createObjectURL(res);
window.open(fileURL);
})
}
Service
printBill(billID) {
return this.http.get('/billing/receipt/'+billID,
{ responseType: ResponseContentType.Blob })
.map((res) => {
return new Blob([res.blob()], { type: 'application/pdf' })
})
}
And dont forget to import ResponseContentType
Hope this helps you
i have implemented it like this.
i have a service requesting file download. The response return a url, which is on amazon s3. This is a zip file containing what i want to download.
the below works on all browsers.
in your controller
requestDownload() {
this.downloadservice.downloadImages(obj)
.subscribe(
response => this.download(response )
);
}
// res is an object
download(res){
var link = document.createElement("a");
link.download = "a";
link.href = res.link;
document.body.appendChild(link);
link.click();
}
downloadservice file
downloadImages(body): Observable<any>{
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post("/Camera51Server/getZippedImages", body, options)
.map((res:Response) => res.json())
.catch(this.handleError);
}
if you like i can give you a link to the repository.