How can I write xlsx to file in browser with ExcelJS? - javascript

I am trying to create an xlsx file in the browser and find https://github.com/exceljs/exceljs very powerful. However, I can't find a way how to save my xlsx object to a file. Probably I need to use Buffer, but how to generate a file from it?
const buffer = await workbook.xlsx.writeBuffer();
This library can generate files in the browser https://docs.sheetjs.com/docs/ but it is not good at building complex fields.

Use FileSaver.js (https://github.com/eligrey/FileSaver.js/)
var FileSaver = require('file-saver');
const buffer = await workbook.xlsx.writeBuffer();
FileSaver.saveAs(new Blob([buffer]), “result.xlsx”);

You can do it without FileSaver.js.
Creating a buffer for ExcelJS:
let buffer = await workbook.xlsx.writeBuffer();
Creating a buffer for SheetJS:
let buffer = XLSX.write(workBook, { bookType: 'xlsx', compression: true, type: 'buffer' });
Saving to a file for both libraries:
let blob = new Blob([buffer], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
let link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = "fName.xlsx";
link.click();
URL.revokeObjectURL(link.href);

Related

How to Force Download to use JPEG instead of JFIF

I'm creating a feature to export a graph from my website. On my local machine the images are being downloaded as JFIF's on my coworkers the images are properly being downloaded as JPEG's. I am aware that I can configure my local machine to change the extension. However, I don't want non-technical users to have to go through this process. Is there a way to ensure that images are downloaded on any machine as JPEG's?
Here is my code
const downloadGraph = () => {
const element = document.getElementById('unique-id') as HTMLCanvasElement;
saveImage(element, 'Title');
}
const saveImage = (canvas: HTMLCanvasElement, fileName: string) => {
const url = canvas.toDataURL('image/jpeg', 1.0);
download(url, fileName, )
}
const download = (url: string, fileName: string) => {
const link = document.createElement("a");
link.setAttribute("href", url);
link.setAttribute("download", fileName);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
Modify the fileName parameter (in both cases)
fileName.slice(0,fileName.lastIndexOf('.'))+'.jpg'

How to download gzip compressed file generated from string with client-sided javascript

I basically have a site that I want to work fully client sided and it needs to be able to receive gziped xml file, decompress it, modify some stuff and then compress it and prompt user to download it.
I have the receiving/decompressing part working using zlib and drag-and-drop-files with browserify, but when I compress file and download it, it can't be decompressed because it apparently has an "invalid header".
Working receiving/decompressing part:
const zlib = require('zlib')
const drop = require('drag-and-drop-files')
const fileReaderStream = require('filereader-stream')
const concat = require('concat-stream')
const $ = require('jquery')
drop(document.getElementById('drop'), function (files) {
var file = files[0]
fileReaderStream(file).pipe(
concat(function (contents) {
var output = zlib.gunzipSync(contents).toString('utf-8')
console.log('File loaded successfully')
var xmlData = $.parseXML(output)
doSomething(xmlData)
})
)
})
And not working compressing/downloading part:
var string = $(this).prop('outerHTML')
var output = zlib.gzipSync(string).toString('utf-8')
var a = document.createElement('a')
a.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(output)
a.download = `${name}.gz`
a.click()
File gets downloaded correctly but it appears to be corrupted. When I skip the compression and just prompt user to download raw xml as text file everything works fine.
I would really appreciate if anyone could tell me how this could be fixed.
Edit 1 - tried using blobs:
var output = zlib.gzipSync(string).toString('utf-8')
var blob = new Blob([output], { type: 'application/gzip' })
var blobUrl = URL.createObjectURL(blob)
var a = document.createElement('a')
a.href = blobUrl
a.download = `${name}.gz`
a.click()
The result is unfortunately the same.
I managed to make the blob version work, it appears that converting to string before creating blob was causing issues.
var output = zlib.gzipSync(string)
var blob = new Blob([output.buffer], { type: 'application/gzip' })
Thanks everyone who suggested to use blob.

Downloading an Azure Storage Blob using pure JavaScript and Azure-Storage-Js

I'm trying to do this with just pure Javascript and the SDK. I am not using Node.js. I'm converting my application from v2 to v10 of the SDK azure-storage-js-v10
The azure-storage.blob.js bundled file is compatible with UMD
standard, if no module system is found, following global variable
will be exported: azblob
My code is here:
const serviceURL = new azblob.ServiceURL(`https://${account}.blob.core.windows.net${accountSas}`, pipeline);
const containerName = "container";
const containerURL = azblob.ContainerURL.fromServiceURL(serviceURL, containerName);
const blobURL = azblob.BlobURL.fromContainerURL(containerURL, blobName);
const downloadBlobResponse = await blobURL.download(azblob.Aborter.none, 0);
The downloadBlobResponse looks like this:
downloadBlobResponse
Using v10, how can I convert the downloadBlobResponse into a new blob so it can be used in the FileSaver saveAs() function?
In azure-storage-js-v2 this code worked on smaller files:
let readStream = blobService.createReadStream(containerName, blobName, (err, res) => {
if (error) {
// Handle read blob error
}
});
// Use event listener to receive data
readStream.on('data', data => {
// Uint8Array retrieved
// Convert the array back into a blob
var newBlob = new Blob([new Uint8Array(data)]);
// Saves file to the user's downloads directory
saveAs(newBlob, blobName); // FileSaver.js
});
I've tried everything to get v10 working, any help would be greatly appreciated.
Thanks,
You need to get the body by await blobBody.
downloadBlobResponse = await blobURL.download(azblob.Aborter.none, 0);
// data is a browser Blob type
const data = await downloadBlobResponse.blobBody;
Thanx Mike Coop and Xiaoning Liu!
I was busy making a Vuejs plugin to download blobs from a storage account. Thanx to you, I was able to make this work.
var FileSaver = require('file-saver');
const { BlobServiceClient } = require("#azure/storage-blob");
const downloadButton = document.getElementById("download-button");
const downloadFiles = async() => {
try {
if (fileList.selectedOptions.length > 0) {
reportStatus("Downloading files...");
for await (const option of fileList.selectedOptions) {
var blobName = option.text;
const account = '<account name>';
const sas = '<blob sas token>';
const containerName = '< container name>';
const blobServiceClient = new BlobServiceClient(`https://${account}.blob.core.windows.net${sas}`);
const containerClient = blobServiceClient.getContainerClient(containerName);
const blobClient = containerClient.getBlobClient(blobName);
const downloadBlockBlobResponse = await blobClient.download(blobName, 0, undefined);
const data = await downloadBlockBlobResponse.blobBody;
// Saves file to the user's downloads directory
FileSaver.saveAs(data, blobName); // FileSaver.js
}
reportStatus("Done.");
listFiles();
} else {
reportStatus("No files selected.");
}
} catch (error) {
reportStatus(error.message);
}
};
downloadButton.addEventListener("click", downloadFiles);
Thanks Xiaoning Liu!
I'm still learning about async javascript functions and promises. Guess I was just missing another "await". I saw that "downloadBlobResponse.blobBody" was a promise and also a blob type, but, I couldn't figure out why it wouldn't convert to a new blob. I kept getting the "Iterator getter is not callable" error.
Here's my final working solution:
// Create a BlobURL
const blobURL = azblob.BlobURL.fromContainerURL(containerURL, blobName);
// Download blob
downloadBlobResponse = await blobURL.download(azblob.Aborter.none, 0);
// In browsers, get downloaded data by accessing downloadBlockBlobResponse.blobBody
const data = await downloadBlobResponse.blobBody;
// Saves file to the user's downloads directory
saveAs(data, blobName); // FileSaver.js

Create a file object from an img tag

Let's say that I have an img tag with a base64 src like :
<img src="data:image/jpeg;base64,/9j/4AAQS..." id="id">
How can I create a File object (containing the data of the img tag) alike an object I would get after an user chose a file from a file input ? In order to upload it to a server later.
Like that one :
File { name: "a22919a70cf8458d654642e0bc2cd881.jpg", lastModified: 1521393615000, lastModifiedDate: Date 2018-03-18T17:20:15.000Z, webkitRelativePath: "", size: 40635, type: "image/jpeg" }
I'm not looking for getting a BLOB.
You could always create a blob first and then convert that to a file.
But you hardly ever need a File instance. It will give you more headache trying to construct them then just using the blob. If you need to upload them to a server you need to use FormData. And when you do you can append a blob and set the filename as a 3th argument fd.append(field, blob, filename) instead of appending a file
The file constructor arguments are the same as blob except between the parts and the options you pass in the filename.
File Object inherits the Blob object so you use them the same way in other apis as well
new File([parts], filename, options)
new Blob([parts], options)
Other thing is that different is file supports 1 more option... lastModifiedIE/Edge can have Files but the constructor don't work. that's why you should use blobs...
const img = document.getElementById('id')
fetch(img.src)
.then(res => res.blob())
.then(blob => {
const file = new File([blob], 'dot.png', blob)
console.log(file)
})
<img id="id" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">
The File Object inherits the Blob object, so it is very close to being the same.
The code below shows a successfully generated File "Blob" object can be used to generate a object url which downloads the image.
( Note: on stack overflow the blob url has a null host which causes it to not to automatically download as a file, but you can still use right click save as. See URL.createObjectURL returns a blob with null prepended. Eg : Blob:null/12415-63 )
// https://bl.ocks.org/nolanlawson/0eac306e4dac2114c752
let dataUrl = document.getElementById("img1").src.split(',')
let base64 = dataUrl[1];
let mime = dataUrl[0].match(/:(.*?);/)[1];
let bin = atob(base64);
let length = bin.length;
// From http://stackoverflow.com/questions/14967647/ (continues on next line)
// encode-decode-image-with-base64-breaks-image (2013-04-21)
let buf = new ArrayBuffer(length);
let arr = new Uint8Array(buf);
bin
.split('')
.forEach((e,i)=>arr[i]=e.charCodeAt(0));
let f = new File([buf],'filename',{type:mime}); // note: [buf]
let blobUrl = URL.createObjectURL(f);
let link = document.createElement("a");
link.href = blobUrl;
link.download = "filename";
link.innerHTML = "Download file.";
document.getElementById("url1").appendChild(link);
<img id="img1" src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAB1klEQVR42n2TzytEURTHv3e8N1joRhZGzJsoCjsLhcw0jClKWbHwY2GnLGUlIfIP2IjyY2djZTHSMJNQSilFNkz24z0/Ms2MrnvfvMu8mcfZvPvuPfdzz/mecwgKLNYKb0cFEgXbRvwV2s2HuWazCbzKA5LvNecDXayBjv9NL7tEpSNgbYzQ5kZmAlSXgsGGXmS+MjhKxDHgC+quyaPKQtoPYMQPOh5U9H6tBxF+Icy/aolqAqLP5wjWd5r/Ip3YXVILrF4ZRYAxDhCOJ/yCwiMI+/xgjOEzmzIhAio04GeGayIXjQ0wGoAuQ5cmIjh8jNo0GF78QwNhpyvV1O9tdxSSR6PLl51FnIK3uQ4JJQME4sCxCIRxQbMwPNSjqaobsfskm9l4Ky6jvCzWEnDKU1ayQPe5BbN64vYJ2vwO7CIeLIi3ciYAoby0M4oNYBrXgdgAbC/MhGCRhyhCZwrcEz1Ib3KKO7f+2I4iFvoVmIxHigGiZHhPIb0bL1bQApFS9U/AC0ulSXrrhMotka/lQy0Ic08FDeIiAmDvA2HX01W05TopS2j2/H4T6FBVbj4YgV5+AecyLk+CtvmsQWK8WZZ+Hdf7QGu7fobMuZHyq1DoJLvUqQrfM966EU/qYGwAAAAASUVORK5CYII=">
<div id="url1"></div>

How to make a browser display a "save as dialog" so the user can save the content of a string to a file on his system?

How can I make a browser display a "save as dialog" so the user can save the content of a string to a file on his system?
For example:
var myString = "my string with some stuff";
save_to_filesystem(myString,"myString.txt");
Resulting in something like this:
EDIT 2022: Please see other answers regarding File System API
In case anyone is still wondering...
I did it like this:
Save
can't remember my source but it uses the following techniques\features:
html5 download attribute
data URI's
Found the reference:
http://paxcel.net/blog/savedownload-file-using-html5-javascript-the-download-attribute-2/
EDIT:
As you can gather from the comments, this does NOT work in
Internet Explorer (however works in Edge v13 and later)
Opera Mini
http://caniuse.com/#feat=download
There is a new spec called the Native File System API that allows you to do this properly like this:
const result = await window.chooseFileSystemEntries({ type: "save-file" });
There is a demo here, but I believe it is using an origin trial so it may not work in your own website unless you sign up or enable a config flag, and it obviously only works in Chrome. If you're making an Electron app this might be an option though.
There is a javascript library for this, see FileSaver.js on Github
However the saveAs() function won't send pure string to the browser, you need to convert it to blob:
function data2blob(data, isBase64) {
var chars = "";
if (isBase64)
chars = atob(data);
else
chars = data;
var bytes = new Array(chars.length);
for (var i = 0; i < chars.length; i++) {
bytes[i] = chars.charCodeAt(i);
}
var blob = new Blob([new Uint8Array(bytes)]);
return blob;
}
and then call saveAs on the blob, as like:
var myString = "my string with some stuff";
saveAs( data2blob(myString), "myString.txt" );
Of course remember to include the above-mentioned javascript library on your webpage using <script src=FileSaver.js>
This is possible using this cross browser javascript implementation of the HTML5 saveAs function: https://github.com/koffsyrup/FileSaver.js
If all you want to do is save text then the above script works in all browsers(including all versions of IE), using nothing but JS.
Solution using only javascript
function saveFile(fileName,urlFile){
let a = document.createElement("a");
a.style = "display: none";
document.body.appendChild(a);
a.href = urlFile;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
a.remove();
}
let textData = `El contenido del archivo
que sera descargado`;
let blobData = new Blob([textData], {type: "text/plain"});
let url = window.URL.createObjectURL(blobData);
//let url = "pathExample/localFile.png"; // LocalFileDownload
saveFile('archivo.txt',url);
Using showSaveFilePicker():
const handle = await showSaveFilePicker({
suggestedName: 'name.txt',
types: [{
description: 'Text file',
accept: {'text/plain': ['.txt']},
}],
});
const blob = new Blob(['Some text']);
const writableStream = await handle.createWritable();
await writableStream.write(blob);
await writableStream.close();
Inspired by #ronald-coarite answer, here is my solution:
function saveTxtToFile(fileName: string, textData: string) {
const blobData = new Blob([textData], { type: 'text/plain' });
const urlToBlob = window.URL.createObjectURL(blobData);
const a = document.createElement('a');
a.style.setProperty('display', 'none');
document.body.appendChild(a);
a.href = urlToBlob;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(urlToBlob);
a.remove();
}
saveTxtToFile('myFile.json', JSON.stringify(myJson));

Categories