FileSaver.saveAs save path change possible? - reactjs

I am working with react. I have a file to download. and its download to browser default save location(Download folder)
FileSaver.saveAs(res.data, fileTitle + "." + fileExtension);
I have found that its not possible to change with FileSaver.saveAs method. So i want to know is there a another method in react to save file to local path.

You can't save a file to a particular path using FileSaver library.
But there's a new File System API you can use to achieve that. There's a project by googlechromelabs, a simple text editor (demo), designed to experiment with and demonstrate the new File System Access APIs.
Create a new file.
To save a file, call showSaveFilePicker(), which shows the file picker in "save" mode, allowing the user to pick a new file they want to use for saving
async function getNewFileHandle() {
const options = {
types: [
{
description: 'Text Files',
accept: {
'text/plain': ['.txt'],
},
},
],
};
const handle = await window.showSaveFilePicker(options);
return handle;
}
Save changes to disk
// fileHandle is an instance of FileSystemFileHandle..
async function writeFile(fileHandle, contents) {
// Create a FileSystemWritableFileStream to write to.
const writable = await fileHandle.createWritable();
// Write the contents of the file to the stream.
await writable.write(contents);
// Close the file and write the contents to disk.
await writable.close();
}

Related

Firebase storage can't move/upload file when change its name, react js, redux

I'm trying to re-upload/move a file is already existed in firebase storage to another path in the same firebase Storage with different folder and changing its name using redux-actions, so the file getting uploaded but it corrupted => it means when I try to open it, it's opening with no data/picture and the size of the picture 9B
the code:
// upload the same file with new path and remove the old file
let uploadAndDeletePromises = fileArr.map(
(fileData, index) =>
storageRef
// newpaths=folderName/FolderWithIdName/fileName__docId.png
.child(newFilesPaths[index])
.put(
// filesToUpload data showed in the pictures below
filesToUpload[index],
// metadata={customMetadata:
// {uplaoderId:"",PrId:""}
metadata[index]
)
.then(() =>
//remove old path
storageRef
// fileData.path -> the old path
// FolderName/folderWithIdName/fileName.png
.child(fileData.path)
.delete()
)
);
return Promise.all(uploadAndDeletePromises);
the result from the filesToUpload from the original one which works well, these are when the first time I upload:
the result from the filesToUpload from the one which I want to re-upload from firebase storage to another path in firestore storage, these are when the I'm trying to re-upload to different path:
Anyone experience handling/moving a file from a path to another and changing its name, using react js actions, firebase storage, not node js.
The functions to move a file are in the server-side libraries. If you want to move a file in the client you'll have to take the following steps:
1. Download the file
2. Upload the file to the new location
3. Delete the previous file (if required)
It looks like you have an idea as to how to upload the new file and delete the previous one, but are having problems downloading the previous file (which is why a 9B file is uploading).
As per the documentation, you would download the file like this
storageRef.child('path/to/file').getDownloadURL()
.then((url) => {
// `url` is the download URL for the file
// This can be downloaded directly:
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = (event) => {
var blob = xhr.response;
// insert upload and delete code here
};
xhr.open('GET', url);
xhr.send();
})
.catch((error) => {
// Handle any errors
});

Download files from firebase storage using ReactJs

Succesfully i have made to Upload files into firebase storage, but now i want to display all files in table and to have option to download each file.I've read the documentation in firebase but it won't work.When i click the button which function is to get all files and the i want to visualize them in table which users can see:
Show file function:
showFileUrl(){
storageRef.child('UploadedFiles/').listAll().then(function(res) {
res.items.forEach(function(folderRef) {
console.log("folderRef",folderRef.toString());
var blob = null;
var xhr = new XMLHttpRequest();
xhr.open("GET", "downloadURL");
xhr.responseType = "blob";
xhr.onload = function()
{
blob = xhr.response;//xhr.response is now a blob object
console.log(blob);
}
xhr.send();
});
}).catch(function(error) {
});
}
This is log of the network which i found when debugging.What i need to do to get all data and visualize it in table and to hava a download button and when is pressed to download the file
Network log:
Storage in firebase:
Blob object of the files:
Your code gets a list of all the files, but it doesn't actually to anything to read the data for each file.
When using the Web client SDK, the only way to get the data for a file is through a download URL as shown here. So you'll need to:
Loop through all the files you get back from listAll() (you're already doing this).
Call `getDownloadURL as shown here, to get a download URL for each file.
Then use another library/function (such as fetch()/XMLHTTPRequest) to read the data for each file.
Alternatively, if your files are images, you can stuff the download URL in an img tag as the preview.

How to send parsed .csv file as a byte array or ArrayBuffer data from Node.js backend to AngularJS frontend?

I'm working on AngularJS app.
Module I'm currently working on should be able to either show a preview of a spreadsheet file or allow to download it.
The steps:
When clicked on "Preview File" it should send request with needed file's name as a parameter of POST request.
Backend will find neede file, which is a .csv file, convert it to byte array type and send it to frontend.
Frontend should handle this byte array and convert it to .xls or .xlsx filetype
The spreadsheet data should be opened in some small preview read-only window, like 1000x1000 px.
The POST request line looks like that:
this.$http.post(this.url + 'endpoint/getFile', params,
{responseType: "arraybuffer", showLoadingOverlay: true}
)
The response looks indeed like ArrayBuffer: three of it in one object, i.e. Uint8Array, Uint16Array and Uint32Array.
The code which should read this and convert to content suitable for preview is not working:
const byteArray = new Uint8Array(data);
const blob = new Blob([byteArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const objectUrl = URL.createObjectURL(blob);
this.$window.open(objectUrl, 'C-Sharpcorner', 'width=1000,height=1000');
Because when created the blob, it already has 0 length in bytes, so there's no data inside.
The matter of visualising the .xls in browser window, I think, can be achieved with canvas-datagrid library. Haven't used but it looks cool.
Also, I have a problem with trying to set up a mock data for node.js (and AngularMock), for local testing when there's no data on a java backend.
I'm using 'fs' and 'csv-parse':
const fs = require('fs');
const csvParse = require("csv-parse/lib/es5");
module.exports = function stir(app) {
const getFile = () => {
const csvOutput = csvParse('../static/someData.csv', (parsed) => {
return parsed;
});
fs.readFileSync(csvOutput);
};
app.post('/stir/getFile', (req, res) => res.json(getFile()));
};
Which results in error:
TypeError: path must be a string or Buffer
What is the proper way of parsing the .csv using 'csv-parse' and sending parsed data as an ArrayBuffer to frontend in Node and AngularMock?
csv-parse docs are telling that underneath, the lib will convert the parsed object to node stream.
So why that error happens?

Is it possible to save a file directly from a web worker?

I have an entirely browser-based (i.e. no backend) application which analyzes XML data in files which average about 250MB each. The actual parsing and analysis happens in a web worker, which is fed data in 64KB chunks by a FileReader instance, and this is all quite performant.
I have a request from the client to expand this application so that it can generate a .zip file containing the original input file and the results of the analysis, and allow the user to save that file to her local machine. Generating a .zip file in memory with those contents isn't a problem. The problem lies in transferring that much data from the web worker which generates it back to the main browser thread, so that it can be saved; attempting to do this invariably provokes a crash or out-of-memory exception. (I've tried transferring strings all at once and a chunk at a time, and I've tried using an ArrayBuffer as a transferable object to avoid copying. All fail in the same fashion.)
Unfortunately, I don't know any way to invoke a file save operation directly from a worker thread. I know several methods of doing so from the main browser thread, but all of them require either the ability to create DOM nodes (which worker threads of course can't do), or the use of interfaces (i.e. msSaveBlob, saveAs) which no browser seems to expose to a worker thread. I've spent a while looking for possibilities on the web, but found nothing usable; FileWriterSync looked good, but only Chrome supports it, and I need to target IE and Firefox as well.
Is there a method I've overlooked for saving files directly from a web worker? If so, what is it? Or am I just out of luck here?
tl;dr demo
You don't need to copy the entire file to the client side at all. You don't even need to transfer it, in fact. First a recap.
This is how to create Blob from some typed array:
// Some arbitrary binary data
const mydata = new Uint16Array([1,2,3,4,5]);
// mydata vs. mydata.buffer does not seem to make any difference
const blob = new Blob([mydata], {type: "octet/stream"});
You can create an object URL, which is a copy of the original Blob managed by the browser and accessible as URL. I have done this with huge files without seeing performance impact:
const url = URL.createObjectURL(blob);
This is how I typically download URLs:
const link = document.createElement("a");
link.download = "data.bin";
link.href = e.data.link;
link.appendChild(new Text("Download data"));
link.addEventListener("click", function() {
this.parentNode.removeChild(this);
// remember to free the object url, but wait until the download is handled
setTimeout(()=>{URL.revokeObjectURL(e.data.link);}, 500)
});
document.body.appendChild(link);
You can trigger the download automatically by invoking click event on that link. I prefer to let the user decide when to download.
So, all together:
worker.js
// Some arbitrary binary data
const mydata = new Uint16Array([1,2,3,4,5]);
self.onmessage = function(e) {
console.log("Message: ",e.data)
switch(e.data.name) {
case "make-download" :
const blob = new Blob([mydata.buffer], {type: "octet/stream"});
const url = URL.createObjectURL(blob);
self.postMessage({name:"download-link", link:url});
break;
default:
console.error("Unknown message:", e.data.name);
}
}
main.js
var worker = new Worker("worker.js");
worker.addEventListener("message", function(e) {
switch(e.data.name) {
case "download-link" : {
if(e.data.error) {
console.error("Download error: ", e.data.error);
}
else {
const link = document.createElement("a");
link.download = "data.bin";
link.href = e.data.link;
link.appendChild(new Text("Download data"));
link.addEventListener("click", function() {
this.parentNode.removeChild(this);
// remember to free the object url, but wait until the download is handled
setTimeout(()=>{URL.revokeObjectURL(e.data.link);}, 500)
});
document.body.appendChild(link);
}
break;
}
default:
console.error("Unknown message:", e.data.name);
}
});
function requestDownload() {
worker.postMessage({name:"make-download"});
}
When I click Download in my demo, I can see this in my HEX editor:
Looks just fine :)

Download file with NodeJS

I am creating a csv file with node js and now want to download it to user's browser's default download location. We are using seneca with nodejs and csv file is getting saved on the server. Now when use will click export on the front end which angular based, node will create a csv and download that to user machine. How can we achieve this?
It is possible to download dynamic files generated with Node.js to the browser's default download location. There are many posts outlining how to retrieve static files from the server using the Express helper res.download(). Specific to your question there is a way to achieve what you are asking.
With interpretation of your question the following process is followed:
User generated data is sent to the server for processing when a user clicks export.
Node processes the data and generates a file that is to be downloaded without a second user interaction (user clicks Export and the file is downloaded).
Client
//Export button
$("#exportBtn").click(function () {
//Code to generate data for the CSV and save it to the src variable
var src = csvData;
//Send the CSV data to Node for processing and file generation.
$.post("http://localhost:3000/submitcsv", { csv: src }, function (data) {
//Check the returned file name in data - this check depends on your needs; ie: regex
if (data) {
//request the file that is to be downloaded to the client. Optionally use $window.open()
window.location.href = "http://localhost:3000/app/" + data;
}
});
});
Server
//Post data from the client
app.post('/submitcsv', function (req, res) {
var async = require('async');
var fs = require('fs');
var path = require('path');
var csvData = req.body.csv;
function processCSV(callback) {
//Code to create the csv file and a uniqueIdentifier
callback();
}
function finalize() {
//Save the CSV to the server. This is a specific location on the server in /app.
//You can use Express.static paths that suit your setup.
fs.writeFile('./app/temp/csvFile' + uniqueIdentifier + '.csv', buf, function (err) {
if (err) {
console.log(err);
res.send(500, "Something went wrong...");
}
else {
console.log("CSV file is saved");
//Send the file name and location back to the client
res.send('/temp/csvFile' + uniqueIdentifier + '.csv');
}
});
}
// After the CSV data is processed, save the file and send it to the client.
async.series([
processCSV
], finalize);
});
//Send the requested file back to the client
app.get('./app/:csvFile', function (req, res){
var c = req.params.csvFile;
res.download(c);
//Code to delete the file if it is temporary via fs.unlink
});
While it is not shown in the above code for simplicty, it is recommended that the /app routes sending and receiving this type of data are secured in some manner. Using Passport OIDCStrategy for the protected routes will look something like:
app.all('/app/*', function(req, res, next) {
//Check that the user has previously been authenticated
if (req.user){
next();
} else {
// require the user to log in
res.redirect("/login");
}
});
I dont think you can... Nodejs is serverside... so anything you put under Nodejs, will go to the server...
If the USER click on some button or some other event triggers a process that generates the CSV file, then the USER will have to choose where they want to save it or the file will be downloaded automatically to the default Downloads directory that was specified under USER's browser settings...

Resources