I'm trying to load in a relative file address into a function previously used by a file reader. On the site, I had a button that would let you pick local files to be loaded into a graphics renderer. I want to use a url to access these files relatively instead, but I can't figure out how to bind them to a file object. I have been using this mozilla documentation to try and figure it out.
Here is the code that was used originally:
// function that takes file input and renders the image
function readFiles(){
// Deal with file input
if (window.File && window.FileReader && window.FileList && window.Blob) {
var file1 = document.getElementById('fileinput').files[0];
var file2 = document.getElementById('fileinput').files[1];
// Call the file analyzer
fileAnalyzer( file1, file2 );
} else {
alert('The File APIs are not fully supported by your browser.');
}
}
Here is the code that I want to update:
// load cube button
var loadcube = document.getElementById('loadcube');
loadcube.onclick = function(evt) {
var file1 = new File([], "Object files/cube3.coor" );
var file2 = new File([], "Object files/cube3.poly" );
fileAnalyzer( file1, file2);
}
You can use the Fetch API to asynchronously fetch the resources, convert them to a Blob, and then construct a File like this:
function fetchAsFile(path) {
return fetch(path).then(function (response) {
return response.blob()
}).then(function (blob) {
return new File([blob], path)
})
}
var loadcube = document.getElementById('loadcube')
loadcube.addEventlistener('click', function () {
var p1 = fetchAsFile('Object files/cube3.coor')
var p2 = fetchAsFile('Object files/cube3.poly')
Promise.all([p1, p2]).then(function (files) {
fileAnalyzer(files[0], files[1])
})
})
Using ES2017 async / await, you can simplify the above like this:
async function fetchAsFile(path) {
let response = await fetch(path)
let blob = await response.blob()
return new File([blob], path)
}
var loadcube = document.getElementById('loadcube')
loadcube.addEventlistener('click', async function () {
let p1 = fetchAsFile('Object files/cube3.coor')
let p2 = fetchAsFile('Object files/cube3.poly')
let files = await Promise.all([p1, p2])
fileAnalyzer(files[0], files[1])
})
Related
I have an array of strings that are links to a URL that I would like to download content from. The downloadLinks array looks like:
['stackoverFlow.com/File0', 'stackoverFlow.com/File1', 'stackoverFlow.com/File2'].
Here is the code that I am using to try and download these files. Example code for both using Request and HTTPS.get
var fs = require ('fs');
var sleep = require('sleep');
var https = require('https');
var request = require('request')
// Using Request:
var uniqueDownloadLinks = [ ...new Set(downloadLinks)]
for (downloadLink in uniqueDownloadLinks) {
const download = (url, callback) => {
request.head(url, (err, response, body) => {
var file = fs.createWriteStream(dest);
var dest = 'files/' + response.headers['content-disposition'].toLowerCase().split('filename=')[1].split(';')[0].replace(/"/g, '');
request(url)
.pipe(file)
.on('close', callback)
})
}
download(uniqueDownloadLinks[downloadLink], () => {
console.log('fileDownloaded')
})
sleep.sleep(5);
}
// Using HTTPS.get
var uniqueDownloadLinks = [ ...new Set(downloadLinks)]
for (downloadLink in uniqueDownloadLinks) {
var download = function(url) {
var request = https.get(url, function(response) {
var dest = 'files/' + response.headers['content-disposition'].toLowerCase().split('filename=')[1].split(';')[0].replace(/"/g, '');
var file = fs.createWriteStream(dest);
response.pipe(file);
file.on('finish', function() {
file.close();
});
});
download(downloadLink);
sleep.sleep(5);
}
I have tested this code outside of the foreach loops in a test.js file including only the code to download a file and it works. The files output, everything is happy. When I try to download using the foreach loops I do not download anything. I suspect this has to do with me trying to download inside of a foreach loop but it is difficult to debug what is going wrong because I never enter the request code (For example trying to use console.log(dest) to output the destination + filename from the content-disposition header.
What should I do to be able to iterate over this array of links, and download each file?
Thanks to the comment made by Estus Flask I looked into the problem with using sleep and determined that he was correct in his assessment that "sleep is awful".
The fix to this involves making the download function async and using a promise to handle the sleep of 5 seconds.
async function sleep(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
}
await sleep(5000)
const $ = await download(uniqueDownloadLinks[downloadLink], () => {
console.log('fileDownloaded')
})
when i get the image from the input
i have to convert it to a buffer to make some operations with the image, so as a result a i have a buffer instead of file.
im using FileCollection in meteor to store the image in mongo collection
uploadIt(e) {
e.preventDefault();
var reader = new FileReader();
var buffer;
var file = e.currentTarget.files[0];
if (e.currentTarget.files && e.currentTarget.files[0]) {
reader.onload = function(e){
buffer = new Uint8Array(reader.result);
// some operations over the buffer
};
reader.readAsArrayBuffer(file);
if (file) {
let uploadInstance = CourseFilesCollection.insert({
file: buffer,
..
..
})
}
}
but when i insert it got this error
message: "[FilesCollection] [insert] Have you forget to pass a File itself?
the code originally was
if (file) {
let uploadInstance = CourseFilesCollection.insert({
file: file,
..
..
})
}
but since i had to perfom operations over the the image i need to someway conver the buffer to file
any ideas how to solve this ?
Short answer
use the file constructor to turn bits back to a file container:
file: new File([buffer], file.name, file)
you could try using blob also with wider browser support... but if you want to use the latest tech, then:
async uploadIt (evt) {
evt.preventDefault()
const file = evt.currentTarget.files[0]
if (!file) return
const buffer = new Uint8Array(await file.arrayBuffer())
// some operations over the buffer
const uploadInstance = CourseFilesCollection.insert({
file: new File([buffer], file.name, file)
})
}
Im trying to do a file upload function to IPFS that returns the string of the url for where the file is located. But I cannot get it to return it. I have tried using var and window to declare the variable but it always returns undefined.Also defined the enlace outside of the function but still it always declare undefined. Its like its not updating the value outside of the function. As the console log after it does print the url. (and yes I know there is some trash code, I have been trying to debug the problem)
function upload() {
const reader = new FileReader();
reader.onloadend = function() {
const ipfs = window.IpfsApi('localhost', 5001) // Connect to IPFS
const buf = buffer.Buffer(reader.result) // Convert data into buffer
ipfs.files.add(buf, (err, result) => { // Upload buffer to IPFS
if(err) {
console.error(err)
return
}
window.enlace = `http://127.0.0.1:8080/ipfs/${result[0].hash}`
console.log(`Url --> ${enlace}`)
console.log(typeof enlace)
//document.getElementById("url").innerHTML= url
//document.getElementById("url").href= url
//document.getElementById("output").src = url
})
}
const photo = document.getElementById("photo");
reader.readAsArrayBuffer(photo.files[0]); // Read Provided File
return window.enlace;
}
I believe ipfs.add is asynchronous, which means return window.enlace executes before window.enlace is set inside of your callback funcion.
I would like process a quite large ZIP file, in which I just would like to extract some PDF files and then upload them to a server. I already have in my app a function that takes an Array of File, send them and get the results of the processing from the server.
My concern is how to set up an equivalent Array of File from the ZIP.
I have tested both zip.js and jszip.js, but I got some strange results.
Here is my code with jszip
async function unzipAndSelect( zipFile ) {
var flist = [];
var zip = await JSZip.loadAsync(zipFile);
await zip.forEach( async function (relativePath, zipEntry) {
var match = zipEntry.name.match( /REGEX.pdf$/ );
if(match) {
var blob = await zip.file( zipEntry.name ).async("blob");
var file = new File( [blob], zipEntry.name, {type : 'application/pdf'} );
flist.push( file );
}
});
console.log( fileList );
return flist;
}
uploadPDFs( fileList ) {
if(fileList.length) {
fileList.forEach( function(f) {
// upload stuff ...
}
}
}
var fileList = unzipAndSelect( "myfile.zip" );
uploadPDFs( fileList );
The fileList seems to be populated but its length remains at zero. When I remove the test on the length, nothing happens. Is my approach correct ? Any help on what happens ?
I have looked at the JSZip internals and, as suggested by #patrick-evans, confirmed that the forEach() method was not Promise aware.
Here is my solution :
async function unzipAndSelect( zipFile ) {
var flist = [];
var zip = await JSZip.loadAsync(zipFile);
// async-forEach loop inspired from jszip source
for(filename in zip.files) {
if (!zip.files.hasOwnProperty(filename)) {
continue;
}
// Object key is the filename
var match = filename.match( /REGEX.pdf$/ );
if(match) {
var blob = await zip.file( filename ).async("blob");
var file = new File( [blob], filename, {type : 'application/pdf'} );
flist.push(file);
}
}
return flist;
}
I'm having a little trouble designing a solution to work with the FileReader API in a react/redux app due to it's asynchronous nature. I've done some looking around but nothing has worked for me.
I have a react component that makes use of react-dropzone to get contents of files and work on it's contents later on. Whenever a file get's dropped with dropzone, a callback fires:
onGuestListDrop = (fileData) => {
const headers =
FileParser.read(fileData)
.getHeaders();
};
The purpose of my internal API design is to get headers of .csv or .xlsx files in order to map the column names to existing system field names. I dont have any trouble parsing the content of the files, I got that working, the problem is with the FileReader.onload event.
My top leve file parser is:
class FileParser {
constructor(file) {
this.reader = new FileReader();
this.init(file[0]);
return this;
}
static read(file) {
return new FileParser(file);
}
init(file) {
switch (file.type) {
case 'text/csv':
this.parser = new CsvParser(this.reader, file);
break;
default:
this.parser = new XlsParser(this.reader, file);
break;
}
}
getHeaders() {
return this.parser.getHeaders();
}
}
The CSV class is:
class CsvParser {
constructor(reader, file) {
this.reader = reader;
this.file = file;
this.parse();
return this;
}
parse() {
this.reader.readAsText(this.file, "UTF-8");
this.reader.onload = function (evt) {
const { result } = evt.target;
this.parsedContent = Papa.parse(result);
console.log(this.parsedContent);
};
}
getHeaders() {
return this.parsedContent.data[0];
}
}
The problem right now is that when I try to access the headers via the getHeaders method I'm getting undefined because FileReader works async. Is there a way to make this work with some refactoring, or is this just not possible ?
I was thinking about using redux actions, but I'm not sure how to connect the parser classes with the store. I thought about passing an action directly to the parser class so I can fire the action creator within the FileReader.onload event. I think this could work, but I'm not sure this is the best approach to work with redux given the circumstances.
Add a deferred pattern in your CsvParser like this.
class CsvParser {
constructor(reader, file) {
this.reader = reader;
this.file = file;
this.deferred = {};
let promise = new Promise((resolve, reject) => {
this.deferred.resolve = resolve;
this.deferred.reject = reject;
});
this.deferred.promise = promise;
this.parse();
return this;
}
parse() {
this.reader.readAsText(this.file, "UTF-8");
this.reader.onload = function (evt) {
const { result } = evt.target;
this.parsedContent = Papa.parse(result);
console.log(this.parsedContent);
this.deferred.resolve(this.parsedContent.data[0])
};
}
getHeaders() {
return this.deferred.promise;
}
Now change your onGuestListDrop method like this
onGuestListDrop = (fileData) => {
let headers = {};
FileParser.read(fileData).getHeaders().then(function (headers) {
headers = headers;
});
};