I am using Forge Crypto Library to hash (SHA256) an uploaded file and output a digest. The file is being hashed fine. But if it is a large file then hashing it might take a little while. So I am trying to display a percentage complete field using FileReader.onprogress. I tried to implement it, but the progress is not being shown. It is just displaying 100.
HTML:
<input id="fileInput" type="file"/>
<label id="label"></label>
JS:
$("#fileInput").change(function() {
hashFiles(this.files);
});
function hashFiles(files) {
for (var i = 0; i < files.length; i++) {
var reader = new FileReader();
reader.onload = function() {
var sha256 = forge_sha256(reader.result, reader.result.length);
alert("SHA256 is " + sha256);
};
reader.onerror = function() {
alert("Could not read the file");
};
var currentFile = files.item(i);
reader.onprogress = function(currentFile) {
if (currentFile.lengthComputable) {
var progress = parseInt(((currentFile.loaded / currentFile.total) * 100), 10);
document.getElementById('label').innerHTML = progress;
}
}
reader.readAsBinaryString(files.item(i));
}
}
http://codepen.io/cavanflynn/pen/vNQPgd?editors=101
Am I implementing .onprogress correctly? I don't think I need the currentFile variable, but I can't figure out how to correctly implement it. Thanks!
Related
With the following input field, the user submits one or multiple HTML files.
<input type="file" id="inputfield" accept="text/html" multiple/>
<div id="get-files">Get Files</div>
When get-files is clicked, how can I get the content of each file on the input field and mess with each file content using the fileReader API?
I tried the following but receive no errors or content.
$("#get-files").on("click", function() { getFilesContent(); });
function getFilesContent() {
var pages = $("#inputfield")[0].files;
// get files data
for (var i = 0; i < pages.length; i++) {
var reader = new FileReader();
reader.onload = (function() {
return function(e) {
console.log($("#inputfield")[0].result);
}
});
reader.readAsText(pages[i]);
}
}
Try this instead
function getFilesContent() {
var pages = $("#inputfield")[0].files;
for (let i = 0; i < pages.length; i++) {
let reader = new FileReader();
reader.onload = function() {
console.log(reader.result);
}
reader.readAsText(pages[i]);
}
}
reader.result contains your HTML data.
I want to change name's file selected by input type=file, but it doesn't work.
This is my code:
$("document").ready(function() {
$('body').on('change', '#FileID', function() {
var name = document.getElementById('FileID');
name.files.item(0).name = Math.floor(Math.random() * 100000);
console.log('Selected file: ' + name.files.item(0).name);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id='FileID' class='class1' type='file' name='Images' value='' />
To change the name of a File object, you need to create a new File instance.
You can create one from a previous File object and it will act a simple wrapper over the data on disk.
The sketchier part is to update the <input type="file"> value to use this new File.
It is now possible through an hack as exposed in this answer of mine:
$('body').on('change', '#FileID', function(evt) {
const newName = Math.floor(Math.random() * 100000);
const input = evt.currentTarget;
const previousFile = input.files[0];
const newFile = new File([previousFile], newName);
// hack to update the selected file
const dT = new DataTransfer();
dT.items.add(newFile);
input.files = dT.files;
console.log('Selected file: ' + input.files.item(0).name);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id='FileID' class='class1' type='file' name='Images' value='' />
However since this part is still mainly an hack, I can't really recommend its use.
So if you don't need to show the updated value in the native input, don't do it. Simply update a custom UI, and use a FormData to upload to your server. The last param of FormData#append(blob, fieldname, filename) will set the filename sent by the browser in the form-data request:
const form = new FormData();
const file = new File(["some data"], "originalname.txt");
form.append("fileField", file, Math.floor(Math.random() * 100000));
console.log("original file's name: ", file.name);
new Response(form).text()
.then((content) => console.log("formdata content: ", content));
So you should not need the aforementioned hacks at all.
For anyone ending here trying to get rid of the file's name, try converting it to base64. this way it won't have the name attached to it and you could upload it how you want. I will leave a code example using reactJS for this.
1: Here is the input file type calling the onChange event with our function:
<input onChange={this.handleBackgroundImagePreview} type="file" accept="image/png,image/gif,image/jpeg"></input>
2: Then convert that image to base64
handleBackgroundImagePreview = (e) =>{
// Retrieves the selected Image or file
const file = e.target.files[0]
//Calling async file reader with a callback
let fileBase64 = '';
this.getBase64(file, (result) => {
fileBase64 = result;
console.log(fileBase64)
//In here you could upload to the server the base 64 data as you want
});
}
getBase64(file, cb) {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
cb(reader.result)
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
}
I am using angular-file-upload directive to upload files, I have problem with images or photos taken with apple devices, I understand that apple include exif orientation data on image to correct view on apple devices, but when these images are uploaded on web app the orientation is wrong for browsers.
I am using this directive in several views on my app, for time reazon change to another like ng-file-upload is not the best option.
There are some other directives that fix this problem like ng-file-upload but I want to know how to fix this issue with onBeforeUploadItem event of angular-file-upload.
Now I solved it.
here's what I do.
Here's the code in html:
<input id="upload_button" type="file" nv-file-select="" uploader="uploader" multiple/>
and
<img ng-show="uploader.isHTML5" ng-src="{{pic}}" height="100%" />
then in controller:
var handleFileSelect = function(evt) {
$scope.pic = "";
var target = evt.dataTransfer || evt.target;
var file = target && target.files && target.files[0];
var options = {
canvas: true
};
var displayImg = function(img) {
$scope.$apply(function($scope) {
$scope.pic = img.toDataURL();
$scope.rotatedFile = dataURItoBlob($scope.pic);
});
};
loadImage.parseMetaData(file, function(data) {
if (data.exif) {
options.orientation = data.exif.get('Orientation');
}
loadImage(file, displayImg, options);
});
};
angular.element(document.querySelector('#upload_button')).on('change', handleFileSelect);
var dataURItoBlob = function(dataURI) {
var binary = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var array = [];
for(var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {type: mimeString});
};`
and then, before upload:
uploader.onBeforeUploadItem = function(item) {
$log.debug('onBeforeUploadItem', item);
item._file = $scope.rotatedFile;
};
and you need loader-image.min.js, ng-img-crop.js and [ngImgCrop] in app.js
And that's it.
I hope it helps you.
I'm working on a simple script for my site to upload images. I have a multiple file input <input type = 'file' name = 'files[]' id = 'hiddenFile' multiple> that is being triggered by a div click. When I queue the files, I want to be able to delete one. I know I can loop through the $('#hiddenFile').val() and splice to get the name out but I'm having an issue with figuring out the file name. When I assign the file to a new img container, how do I get the name? I've tried console.log(f.name) and a few variations but it returns an undefined error. Here are my scripts. I think I'm pretty close but this is something I'm learning as I go. Thanks!
function readURL(input) {
var files = $('#hiddenFile')[0].files; //where files would be the id of your multi file input
//or use document.getElementById('files').files;
for (var i = 0, f; f = files[i]; i++) {
var reader = new FileReader();
reader.onload = function (e) {
console.log(f.name); //how can I get the
//file name here to add it to the image as an attribute?
$("<img src = '"+e.target.result+"' class = 'addedImg'>").appendTo("#imgBox");
};
reader.readAsDataURL(f);
}
}
$(document).ready(function(){
$(document).on('click', '#addBtn', function(e){
e.preventDefault();
$('#hiddenFile').click();
});
});
Try using change event , defining f within an IIFE , setting title attribute value to f.name
$(document).ready(function() {
$(document).on('click', '#addBtn', function(e) {
e.preventDefault();
$('#hiddenFile').click();
});
$("#hiddenFile").change(function(event) {
var files = this.files;
var i = 0,
len = files.length;
(function readFile(n) {
var reader = new FileReader();
var f = files[n];
reader.onload = function(e) {
console.log(f.name);
$("<img src=" + e.target.result + " class=addedImg title=" + f.name + ">")
.appendTo("#imgBox");
// if `n` is less than `len` ,
// call `readFile` with incremented `n` as parameter
if (n < len -1) readFile(++n)
};
reader.readAsDataURL(f); // `f` : current `File` object
}(i)); // `i` : `n` within immediately invoked function expression
})
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div id="addBtn">click</div>
<input type="file" style="display:none" id="hiddenFile" multiple />
<div id="imgBox"></div>
The FileReader object itself does not have access to the file name. You get the file name while you're iterating over the files list as you are doing in your for loop.
var reader = new FileReader();
reader.onload = function (e) {
//update image src or something
};
for (var i = 0, f; f = files[i]; i++) {
reader.readAsDataURL(f); //updates image src or something
//additional method to do something with file name goes here
}
And if you really want to have one method that does those two things in the for loop, then you can wrap it all up in a closure like #ebidel does in his answer here - Get filename after filereader asynchronously loaded a file#answer-12547471.
I am trying to do a Sha256 on a file in Javascript. I used FileReader(HTML5) to read in a file. I use the readAsBinaryString function in the FileReader to pass in the images file. Then on the FileReader.onload function I pass in the evt.target.result to the SHA256 method in the CryptoJs API. I am able to successfully get a hash value but it is not correct. When I pass in a text file, it works fine but not image file.
Code(Should be able to copy the code below to a HTML file and run it on firefox - press the "entire file" button):
<style>
#byte_content {
margin: 5px 0;
max-height: 100px;
overflow-y: auto;
overflow-x: hidden;
}
#byte_range { margin-top: 5px; }
</style>
<input type="file" id="files" name="file" /> Read bytes:
<span class="readBytesButtons">
<button data-startbyte="0" data-endbyte="4">1-5</button>
<button data-startbyte="5" data-endbyte="14">6-15</button>
<button data-startbyte="6" data-endbyte="7">7-8</button>
<button>entire file</button>
</span>
<div id="byte_range"></div><BR>
<div id="byte_content"></div><BR>
<div id="crypto_sha256"></div>
<script src="http://crypto-js.googlecode.com/svn/tags/3.0.2/build/rollups/sha256.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.0.2/build/components/enc-base64-min.js"></script>
<script>
var sha256;
function readBlob(opt_startByte, opt_stopByte) {
var files = document.getElementById('files').files;
if (!files.length) {
alert('Please select a file!');
return;
}
var file = files[0];
var start = parseInt(opt_startByte) || 0;
var stop = parseInt(opt_stopByte) || file.size - 1;
var reader = new FileReader();
// If we use onloadend, we need to check the readyState.
reader.onloadend = function(evt) {
if (evt.target.readyState == FileReader.DONE) { // DONE == 2
document.getElementById('byte_content').textContent = evt.target.result;
document.getElementById('byte_range').textContent =
['Read bytes: ', start + 1, ' - ', stop + 1,
' of ', file.size, ' byte file'].join('');
//**UPDATED SOLUTION: Since its binary data, the message needs to be converted from string to bytes using Latin1**
sha256.update(CryptoJS.enc.Latin1.parse(evt.target.result));
var hash = sha256.finalize();
document.getElementById('crypto_sha256').textContent = ['SHA-256: ', hash].join('');
}
};
var blob = file.slice(start, stop + 1);
reader.readAsBinaryString(blob);
}
document.querySelector('.readBytesButtons').addEventListener('click', function(evt) {
if (evt.target.tagName.toLowerCase() == 'button') {
var startByte = evt.target.getAttribute('data-startbyte');
var endByte = evt.target.getAttribute('data-endbyte');
sha256 = CryptoJS.algo.SHA256.create();
readBlob(startByte, endByte);
}
}, false);
</script>
Sample outputs:
Testing a "text" file:
SHA256 generated by Javascript:
78cb5e86455dc8e3bc20fe17e0213a938281216d57b31f8307f5bca67c37bb09
SHA256 generated by cygwin on the same file:
$ sha256sum Phillips.txt
78cb5e86455dc8e3bc20fe17e0213a938281216d57b31f8307f5bca67c37bb09 *SomeTestFile.txt
Testing a "binary" file(pdf):
SHA256 generated by Javascript:
57f93c1d20a8ad8ade984a1d9756c1a40600bd8a7527601945c4e0b5e042c605
SHA256 generated by cygwin on the same file:
$ sha256sum Smoke\ Detector\ Brochure.pdf
57f93c1d20a8ad8ade984a1d9756c1a40600bd8a7527601945c4e0b5e042c605 *Smoke Detector Brochure.pdf
I know this question is quite old but I figured I'd share what I know anyways.
You can do this more easily by doing what I discuss in this answer https://stackoverflow.com/a/17848266/2226207
Basically you can include components/lib-typedarrays-min.js and then do the following in code.
var reader = new FileReader();
// If we use onloadend, we need to check the readyState.
reader.onloadend = function(evt) {
if (evt.target.readyState == FileReader.DONE) { // DONE == 2
var wordArray = CryptoJS.lib.WordArray.create(e.target.result);
var hash = CryptoJS.SHA256(wordArray);
}
};
var blob = file.slice(start, stop + 1);
reader.readAsArrayBuffer(blob);
I haven't tested the above solution but it should work fine.
Here is a simple solution found : https://code.google.com/p/crypto-js/issues/detail?id=62
When you pass a string to a hasher, it's converted to bytes using UTF-8. That's to ensure foreign characters are not clipped. Since you're working with binary data, you'll want to convert the string to bytes using Latin1.
sha256.update(CryptoJS.enc.Latin1.parse(evt.target.result));