Why my ZIP file from NodeJS server is not readable? - javascript

I'm making a web service that will return some generated zip files to a client.
Currently I did a really simple code for my test and when I query directly this service the ZIP file is well returned. In a near future, I'll need to pass some parameters through the header and so a simple link on the client side will not do the job.
So I decided to use XmlHttpRequest on my client to query the resource and download it as a simple file. The problem comes here, when I generate the download with the server response, the zip file doesn't work.
Here is the NodeJS code (with express) :
.get('/myservice', function(req, res)
{
res.setHeader('Content-Type', 'application/zip');
const JSZip = require('jszip');
const zip = new JSZip();
zip.file('hello.txt', 'Hello world\n');
zip
.generateAsync({type: 'nodebuffer'})
.then(function(content)
{
res.send(content)
}.bind(res));
}
Here is the client side Javascript that call the service and then make a file from the answer :
var req = new XMLHttpRequest();
req.open("GET", "https://mydomain/myservice", false);
req.send(null);
function download(filename, text)
{
var element = document.createElement('a');
element.setAttribute('href', 'data:application/zip;charset=base64,' + text);
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
console.log(req.responseText)
download('test.zip', req.responseText);
I think that I completely miss understand the encoding on the client, but I didn't find the solution.
The downloaded file, when unzip, give me a file that contain the same zip that also do the same thing.
When I call the service with XmlHttpRequest, it is well called and does not throw any error.
I'm using JSZip to generate the zip.
I precise that I can't use blob on generateAsync from JSZip because that throw me an error.
Any solution ?

You dont need to make it so complicated. As you are piping a file already. Just use window.open('/myservice'); as the browser will handle the fact its a file and keep the current page open and download the file.
https://codesandbox.io/s/hardcore-euclid-9nkft

Related

Print file on client side

I am using FileSaver.js to print an object array on the client side (HTML/Typescript).
var blob = new Blob([JSON.stringify( marray)], {type: "text/plain;charset=utf-8"});
saveAs(blob, "Data.txt");
It works fine. The problem is it downloads in the download folder (by default). I want to add a file path along with its name. Any idea? Or another way to do this job. fs is not working in this case. it is not recognizing fs and gives the error fs.writefilesync is not a function
How do you know what path the user has?
It is not safe. There is no way to "climb" the user's file system
If you open the source code, you will see the simplest implementation scheme there. It's just a click on a link download
var a = document.createElement('a')
// ...
a.href = blob
// ...
a.dispatchEvent(new MouseEvent('click'))

How to download a file in the browser directly from the node.js server side without any variable?

My app is created with mean and I am a user of docker too. The purpose of my app is to create and download a CSV file. I already created my file, compressed it and placed it in a temp folder (the file will be removed after the download). This part is in the nodejs server side and works without problems.
I already use several things like (res.download) which is supposed to download directly the file in the browser but nothing append. I tried to use blob in the angularjs part but it doesn't work.
The getData function creates and compresses the file (it exists I can reach it directly when I look where the app is saved).
exports.getData = function getData(req, res, next){
var listRequest = req.body.params.listURL;
var stringTags = req.body.params.tagString;
//The name of the compressed CSV file
var nameFile = req.body.params.fileName;
var query = url.parse(req.url, true).query;
//The function which create the file
ApollineData.getData(listRequest, stringTags, nameFile)
.then(function (response){
var filePath = '/opt/mean.js/modules/apolline/client/CSVDownload/'+response;
const file = fs.createReadStream(filePath);
res.download(filePath, response);
})
.catch(function (response){
console.log(response);
});
};
My main problem is to download this file directly in the browser without using any variable because it could be huge (like several GB). I want to download it and then delete it.
There is nothing wrong with res.download
Probably the reason why res.download don't work for you is b/c you are using AJAX to fetch the resource, Do a regular navigation. Or if it requires some post data and another method: create a form and submit.

Use javascript to generate a file to be downloaded. How to use the 'Save As' dialogue without HTTP headers?

I'm using this function to download a file:
function download(fileName, data) {
fileName = fileName.replace(/\s+/gi, '_').replace(/[^a-zA-Z0-9-.,_]/gi, '');
let blob = new Blob([data], {type: 'text/plain'});
if(window.navigator.msSaveOrOpenBlob){
window.navigator.msSaveBlob(blob, fileName);
}else{
let elem = window.document.createElement('a');
elem.href = window.URL.createObjectURL(blob);
elem.download = fileName;
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
}
}
// Example usage
$('button').on('click', function(){
download("test.txt", "test");
});
It works perfectly fine. However, it seems to download the file directly into the Downloads folder.
How can I make it open a 'Save As' dialogue instead, so the user can choose his preferred download location?
JSFiddle: https://jsfiddle.net/kbwrcL14/
I've seen suggestions before about manipulating the header like settiing a Content-Disposition -- however is that possible when you generate a file on-the-fly? Afaik headers are what is sent before the HTTP body, but by the time the JS code is running, the entire HTTP request has already finished, and the file is generated by the client itself.
Is there a way to make this work without using http headers?

How do I use fs.writeFile in HTML (using node.JS)?

So I've been searching for hours on this topic, and still have not found any real way to achieve what I'm trying to do here.
Simply, I just want to write some text to a text file through the use of a button in HTML. This is what I have:
<!DOCTYPE html>
<html>
<head>
<title>TEST</title>
</head>
<body>
<button onclick="write_text()">WRITE FILE</button>
<script>
function write_text(){
var fs = require('fs');
fs.writeFile("test.txt", "okay this is epic", function(err){
if (err) return console.log(err);
console.log("Nice");
});
};
</script>
</body>
</html>
I'm unsure as to why this doesn't work, do I need to make a separate .js file for the function that the button references?
Any help would be appreciated,
Thanks.
EDIT: I'm trying to save the file to my GoDaddy server so that I can access it later, not just download the file. Testing it locally, it should create a file in the directory of my html document.
As was stated before, you do not do it right.
it is very important to say that node.js is a runtime environment, and simply putting an HTML file with JS code on GoDaddy does not make it "server-side", since the code runs on the browser and not on the server.
What you really want to do is either using Blob as stated before or doing something like this (if you want to use node.js):
var express = require('express')
var app = express()
app.post('/<your_path>', function (req, res) {
writeToFilexx(/*Here you may want to pass data using body parser*/)
})
/*
Here you will start the server
*/
Please note, writeToFilexx is a function you have to implement using fs.
on the client side, you will have to send the server a request with the data you want to write to a file.
It goes like this:
1. client sends data to the server.
2. server gets data (handles the request).
3. server process, and stores data.
As you have probably guessed, the file will be saved on the server, not on client's PC.
You can't do it in the way you want (not server side, no node, then no fs in this context), but there is a workaround creating a blob:
function downloadURL(url, name) {
var link = document.createElement("a");
link.download = name;
link.href = url;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
delete link;
}
function downloadFile() {
var data = "okay this is epic";
var blob = new Blob([data], {type: 'text/txt'});
var url = window.URL.createObjectURL(blob);
downloadURL(url, "test.txt");
}
Call downloadFile() in your button.

Is there a way to download any kind of file using angular filesaver?

I'm using node.js and angular.js for my app and I'm trying to download files through the browser using Blob and fileSaver.js.
I've used these in other sections of my app to download text files and pdf files specifying the correct type when creating the Blob object without any problem, but in the current section I need to support any type of file and I don't know if it's possible.
For example, I've tried downloading an image file with and without type:image/png and the result was a corrupted image - inspecting it in a text editor and comparing it with the original file shows that many of the bytes were changed.
Here are the code snippets I use:
Server:
fs.readFile(/* snipped file path */, function(err, data){
if(err){
/* handle error */
}
else{
res.send(data);
}
});
Client:
$http.get(/* endPoint URL */)
.success(function(result){
var data = new Blob([result], {type: 'image/png'});
FileSaver.saveAs(data, filename);
});
A few questions:
Do I need to specify type for Blob? If so, do I need to specify it at server, too (it's a pain to determine it)? Can't I just skip it on both ends?
What causes the image test to result in corrupted file? Am I missing some content-type header or something?
Try adding {contentType: 'arraybuffer'} to your GET request and remove type from Blob definition, like so:
$http.get(/* endPoint URL */, {contentType: 'arraybuffer'})
.success(function(result){
var data = new Blob([result]);
FileSaver.saveAs(data, filename);
});
(Edit: deleted redundant type definition from Blob)

Categories