How to Force Download to use JPEG instead of JFIF - javascript

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'

Related

Download multiple images from array of URLs

I want to download all images from array of urls, with the function below.
But sometimes even if download starts, the file on i try to open is empty.
The files will be stored on AWS, right now i'm only trying local testing.
const pictureSelected = [
"https://hatrabbits.com/wp-content/uploads/2017/01/random.jpg",
"https://images.unsplash.com/photo-1494253109108-2e30c049369b?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MTB8fHJhbmRvbXxlbnwwfHwwfHw%3D&w=1000&q=80",
];
const handleDownload = () => {
pictureSelected.forEach(async (url) => {
const image = await fetch(url, {
mode: "no-cors",
});
const imageBlog = await image.blob();
const imageURL = URL.createObjectURL(imageBlog);
const link = document.createElement("a");
link.href = imageURL;
link.download = url;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
};
Getting one picture only, and not even able to open it
Are you sure your source is not using cors?
Instead of {mode: 'no-cors'} try using {mode: 'cors'}

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

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);

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.

Download PDF From blob returned by AJAX call [duplicate]

I have a difficult question to you, which i'm struggling on for some time now.
I'm looking for a solution, where i can save a file to the users computer, without the local storage, because local storage has 5MB limit. I want the "Save to file"-dialog, but the data i want to save is only available in javascript and i would like to prevent sending the data back to the server and then send it again.
The use-case is, that the service im working on is saving compressed and encrypted chunks of the users data, so the server has no knowledge whats in those chunks and by sending the data back to the server, this would cause 4 times traffic and the server is receiving the unencrypted data, which would render the whole encryption useless.
I found a javascript function to save the data to the users computer with the "Save to file"-dialog, but the work on this has been discontinued and isnt fully supported. It's this: http://www.w3.org/TR/file-writer-api/
So since i have no window.saveAs, what is the way to save data from a Blob-object without sending everything to the server?
Would be great if i could get a hint, what to search for.
I know that this works, because MEGA is doing it, but i want my own solution :)
Your best option is to use a blob url (which is a special url that points to an object in the browser's memory) :
var myBlob = ...;
var blobUrl = URL.createObjectURL(myBlob);
Now you have the choice to simply redirect to this url (window.location.replace(blobUrl)), or to create a link to it. The second solution allows you to specify a default file name :
var link = document.createElement("a"); // Or maybe get it from the current document
link.href = blobUrl;
link.download = "aDefaultFileName.txt";
link.innerHTML = "Click here to download the file";
document.body.appendChild(link); // Or append it whereever you want
FileSaver.js implements saveAs for certain browsers that don't have it
https://github.com/eligrey/FileSaver.js
Tested with FileSaver.js 1.3.8 tested on Chromium 75 and Firefox 68, neither of which have saveAs.
The working principle seems to be to just create an <a element and click it with JavaScript oh the horrors of the web.
Here is a demo that save a blob generated with canvas.toBlob to your download folder with the chosen name mypng.png:
var canvas = document.getElementById("my-canvas");
var ctx = canvas.getContext("2d");
var pixel_size = 1;
function draw() {
console.log("draw");
for (x = 0; x < canvas.width; x += pixel_size) {
for (y = 0; y < canvas.height; y += pixel_size) {
var b = 0.5;
ctx.fillStyle =
"rgba(" +
(x / canvas.width) * 255 + "," +
(y / canvas.height) * 255 + "," +
b * 255 +
",255)"
;
ctx.fillRect(x, y, pixel_size, pixel_size);
}
}
canvas.toBlob(function(blob) {
saveAs(blob, 'mypng.png');
});
}
window.requestAnimationFrame(draw);
<canvas id="my-canvas" width="512" height="512" style="border:1px solid black;"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js"></script>
Here is an animated version that downloads multiple images: Convert HTML5 Canvas Sequence to a Video File
See also:
how to save canvas as png image?
JavaScript: Create and save file
HERE is the direct way.
canvas.toBlob(function(blob){
console.log(typeof(blob)) //let you have 'blob' here
var blobUrl = URL.createObjectURL(blob);
var link = document.createElement("a"); // Or maybe get it from the current document
link.href = blobUrl;
link.download = "image.jpg";
link.innerHTML = "Click here to download the file";
document.body.appendChild(link); // Or append it whereever you want
document.querySelector('a').click() //can add an id to be specific if multiple anchor tag, and use #id
}, 'image/jpeg', 1); // JPEG at 100% quality
spent a while to come upto this solution, comment if this helps.
Thanks to Sebastien C's answer.
this node dependence was more utils fs-web;
npm i fs-web
Usage
import * as fs from 'fs-web';
async processFetch(url, file_path = 'cache-web') {
const fileName = `${file_path}/${url.split('/').reverse()[0]}`;
let cache_blob: Blob;
await fs.readString(fileName).then((blob) => {
cache_blob = blob;
}).catch(() => { });
if (!!cache_blob) {
this.prepareBlob(cache_blob);
console.log('FROM CACHE');
} else {
await fetch(url, {
headers: {},
}).then((response: any) => {
return response.blob();
}).then((blob: Blob) => {
fs.writeFile(fileName, blob).then(() => {
return fs.readString(fileName);
});
this.prepareBlob(blob);
});
}
}
From a file picker or input type=file file chooser, save the filename to local storage:
HTML:
<audio id="player1">Your browser does not support the audio element</audio>
JavaScript:
function picksinglefile() {
var fop = new Windows.Storage.Pickers.FileOpenPicker();
fop.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.musicLibrary;
fop.fileTypeFilter.replaceAll([".mp3", ".wav"]);
fop.pickSingleFileAsync().then(function (file) {
if (file) {
// save the file name to local storage
localStorage.setItem("alarmname$", file.name.toString());
} else {
alert("Operation Cancelled");
}
});
}
Then later in your code, when you want to play the file you selected, use the following, which gets the file using only it's name from the music library. (In the UWP package manifest, set your 'Capabilites' to include 'Music Library'.)
var l = Windows.Storage.KnownFolders.musicLibrary;
var f = localStorage.getItem("alarmname$").toString(); // retrieve file by name
l.getFileAsync(f).then(function (file) {
// storagefile file is available, create URL from it
var s = window.URL.createObjectURL(file);
var x = document.getElementById("player1");
x.setAttribute("src", s);
x.play();
});

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