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'}
Related
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'
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.
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
Hi Unsplash allows to load random image from their website via:
https://source.unsplash.com/random/2560x1440
if I access the url from the browser each time the url generates random static image eg:
https://images.unsplash.com/photo-1488616268114-d949a6d72edf?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2560&h=1440&fit=crop&s=a7f66c8417bcf79d2503b84db64c7b1a
I would like to request the image in jquery or js via the first url and in response get the second one. Is this possible?
You can use the responseURL of the XMLHttpRequest.
MDN Reference
and this answer for an example in jQuery and native JS.
This is tricky since there are several things going on.
If you use Chrome or Firefox, open the developer tools and review the network tab you will see that the original request returns an HTTP 302 redirect to a different URL:
The browser then follows the specified Location header and you get the page that you see. The image is within an <img> on that page.
So to get the final image you need to:
Do an ajax request to the initial URL
Parse the response headers to get the new location
Do an ajax request to the new location
Parse the HTML in the new response to grab the actual image URL out of the img tag.
Good luck!
You can use PerformanceObserver to get the name property value of the requested resource
const key = "unsplash";
const url = `https://source.${key}.com/random/2560x1440`;
let bloburl = void 0;
let img = new Image;
const getResourceName = () => {
let resource = "";
const observer = new PerformanceObserver((list, obj) => {
// alternatively iterate all entries, check `"name"`
// property value for `key`, break loop if specific resource requested
for (let entry of list.getEntries()) {
let {name: res} = entry.toJSON();
resource = res;
}
});
observer.observe({
entryTypes: ["resource"]
});
return fetch(url)
.then(response => response.blob())
.then(blob => {
observer.disconnect();
bloburl = URL.createObjectURL(blob);
img.src = bloburl;
document.body.appendChild(img);
return resource
})
}
getResourceName().then(res => console.log(res)).catch(err => console.log(err))
You can alternatively use Response.url
const key = "unsplash";
const url = `https://source.${key}.com/random/2560x1440`;
let bloburl = void 0;
let img = new Image;
const getResourceName = fetch(url)
.then(response => Promise.all([response.url, response.blob()]))
.then(([resource, blob]) => {
bloburl = URL.createObjectURL(blob);
img.src = bloburl;
document.body.appendChild(img);
return resource
});
getResourceName.then(res => console.log(res)).catch(err => console.log(err))
fetch('https://source.unsplash.com/random').then(res=>{console.log(res)})
from source worked for me.
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));