Reading file contents on the client-side in javascript in various browsers - javascript

I'm attempting to provide a script-only solution for reading the contents of a file on a client machine through a browser.
I have a solution that works with Firefox and Internet Explorer. It's not pretty, but I'm only trying things at the moment:
function getFileContents() {
var fileForUpload = document.forms[0].fileForUpload;
var fileName = fileForUpload.value;
if (fileForUpload.files) {
var fileContents = fileForUpload.files.item(0).getAsBinary();
document.forms[0].fileContents.innerHTML = fileContents;
} else {
// try the IE method
var fileContents = ieReadFile(fileName);
document.forms[0].fileContents.innerHTML = fileContents;
}
}
function ieReadFile(filename)
{
try
{
var fso = new ActiveXObject("Scripting.FileSystemObject");
var fh = fso.OpenTextFile(filename, 1);
var contents = fh.ReadAll();
fh.Close();
return contents;
}
catch (Exception)
{
return "Cannot open file :(";
}
}
I can call getFileContents() and it will write the contents into the fileContents text area.
Is there a way to do this in other browsers?
I'm most concerned with Safari and Chrome at the moment, but I'm open to suggestions for any other browser.
Edit: In response to the question, "Why do you want to do this?":
Basically, I want to hash the file contents together with a one-time-password on the client side so I can send this information back as a verification.

Edited to add information about the File API
Since I originally wrote this answer, the File API has been proposed as a standard and implemented in most browsers (as of IE 10, which added support for FileReader API described here, though not yet the File API). The API is a bit more complicated than the older Mozilla API, as it is designed to support asynchronous reading of files, better support for binary files and decoding of different text encodings. There is some documentation available on the Mozilla Developer Network as well as various examples online. You would use it as follows:
var file = document.getElementById("fileForUpload").files[0];
if (file) {
var reader = new FileReader();
reader.readAsText(file, "UTF-8");
reader.onload = function (evt) {
document.getElementById("fileContents").innerHTML = evt.target.result;
}
reader.onerror = function (evt) {
document.getElementById("fileContents").innerHTML = "error reading file";
}
}
Original answer
There does not appear to be a way to do this in WebKit (thus, Safari and Chrome). The only keys that a File object has are fileName and fileSize. According to the commit message for the File and FileList support, these are inspired by Mozilla's File object, but they appear to support only a subset of the features.
If you would like to change this, you could always send a patch to the WebKit project. Another possibility would be to propose the Mozilla API for inclusion in HTML 5; the WHATWG mailing list is probably the best place to do that. If you do that, then it is much more likely that there will be a cross-browser way to do this, at least in a couple years time. Of course, submitting either a patch or a proposal for inclusion to HTML 5 does mean some work defending the idea, but the fact that Firefox already implements it gives you something to start with.

In order to read a file chosen by the user, using a file open dialog, you can use the <input type="file"> tag. You can find information on it from MSDN. When the file is chosen you can use the FileReader API to read the contents.
function onFileLoad(elementId, event) {
document.getElementById(elementId).innerText = event.target.result;
}
function onChooseFile(event, onLoadFileHandler) {
if (typeof window.FileReader !== 'function')
throw ("The file API isn't supported on this browser.");
let input = event.target;
if (!input)
throw ("The browser does not properly implement the event object");
if (!input.files)
throw ("This browser does not support the `files` property of the file input.");
if (!input.files[0])
return undefined;
let file = input.files[0];
let fr = new FileReader();
fr.onload = onLoadFileHandler;
fr.readAsText(file);
}
<input type='file' onchange='onChooseFile(event, onFileLoad.bind(this, "contents"))' />
<p id="contents"></p>

There's a modern native alternative: File implements Blob, so we can call Blob.text().
async function readText(event) {
const file = event.target.files.item(0)
const text = await file.text();
document.getElementById("output").innerText = text
}
<input type="file" onchange="readText(event)" />
<pre id="output"></pre>
Currently (September 2020) this is supported in Chrome and Firefox, for other Browser you need to load a polyfill, e.g. blob-polyfill.

Happy coding!
If you get an error on Internet Explorer, Change the security settings to allow ActiveX
var CallBackFunction = function(content) {
alert(content);
}
ReadFileAllBrowsers(document.getElementById("file_upload"), CallBackFunction);
//Tested in Mozilla Firefox browser, Chrome
function ReadFileAllBrowsers(FileElement, CallBackFunction) {
try {
var file = FileElement.files[0];
var contents_ = "";
if (file) {
var reader = new FileReader();
reader.readAsText(file, "UTF-8");
reader.onload = function(evt) {
CallBackFunction(evt.target.result);
}
reader.onerror = function(evt) {
alert("Error reading file");
}
}
} catch (Exception) {
var fall_back = ieReadFile(FileElement.value);
if (fall_back != false) {
CallBackFunction(fall_back);
}
}
}
///Reading files with Internet Explorer
function ieReadFile(filename) {
try {
var fso = new ActiveXObject("Scripting.FileSystemObject");
var fh = fso.OpenTextFile(filename, 1);
var contents = fh.ReadAll();
fh.Close();
return contents;
} catch (Exception) {
alert(Exception);
return false;
}
}

This works fine
function onClick(event) {
filecontent = "";
var myFile = event.files[0];
var reader = new FileReader();
reader.addEventListener('load', function (e) {
filecontent = e.target.result;
});
reader.readAsBinaryString(myFile);
}

Related

Get base64 string of an image and store to hidden element

I need to send a base64 encoded string of images uploaded by a user to some URL for processing.
Due to some reason (my old API), I need that string in a paragraph element and use its content in a Java class to attach string in response(which you can ignore, I have to do it).
HTML:
<input type="file" name="attachment" id="attachment" accept="image/*"
onchange='onChooseFile(event, onFileLoad.bind(this, "imageData"))'>
<p id="imageData"></p>
Javascript:
function onFileLoad(elementId, event) {
document.getElementById(elementId).value = event.target.result;
}
function onChooseFile(event, onLoadFileHandler) {
if (typeof window.FileReader !== 'function')
throw ("The file API isn't supported on this browser.");
let input = event.target;
if (!input)
throw ("The browser does not properly implement the event object");
if (!input.files)
throw ("This browser does not support the `files` property of the file input.");
if (!input.files[0])
return undefined;
let file = input.files[0];
let fr = new FileReader();
fr.onload = onLoadFileHandler;
fr.readAsText(file);
}
The data going on the server is causing the image to be somehow corrupted(getting an error on opening and server code is fine because it's working with other sources of base64 encoded images).
Can you please point out how to do it, I'm not a front-end developer, please forgive me for my naive mistakes.
I used below code instead on those two javascript methods
function onChooseFile(element) {
let file = element.files[0];
let reader = new FileReader();
reader.onloadend = function() {
document.getElementById("imageData").value = reader.result;
}
reader.readAsDataURL(file);
}
worked like a charm

Javascript FileReader doesn't fire events on large files

I'm trying to play the video files from and on client's computer. The thing is it's pretty difficult to get the absolute path so I can put it in video's src or object's data attribute. Eventually I find the FileReader object, and it works on small files pretty well. However, it doesn't fire the onload event when reading large files (200MB+ so far). No error, FileReader.onerror doesn't fire anything, try/catch doesn't help. Developer console doesn't show anything.
I think it has something to do with max file size on each browser's config, but I can't find the way to configure that. Help?
Here's the code
function onclick()
{
var file_dialog = document.getElementById("file_dialog");
var path_dialog = document.getElementById("path_dialog");
var video_player = document.getElementById("video_player");
var begin_video = document.getElementById("begin_video");
var reading_progress = document.getElementById("reading_progress");
file_dialog.onchange = function ()
{
begin_video.disabled = file_dialog.files.length == 0 || path_dialog.files.length == 0;
};
path_dialog.onchange = file_dialog.onchange;
begin_video.onclick = function ()
{
begin_video.disabled = true;
var reader = new FileReader();
reader.onload = function (e)
{
video_player.src = e.target.result;
begin_video.disabled = false;
};
reader.onprogress = function (e)
{
reading_progress.textContent = "Reading... " + (Math.floor(e.loaded / e.total * 10000) / 100) + "%";
};
reader.onloadend = function (e)
{
if (e.target.error != null)
reading_progress.textContent = e.target.error.code;
else
reading_progress.textContent = "FINISHED!!!";
};
reader.onerror = alert;
reader.readAsDataURL(file_dialog.files[0]);
var reader2 = new FileReader();
reader2.onload = function (e)
{
};
};
}
There is no such browser config setting for this.
I've also worked with FileReader and large files (up to 50 MB) and the browsers behave very different:
Chrome => did well and was the most "responsive"
Firefox => did not as well as Chrome, high memory consumption, but worked
IE => worked as long the file was below 15 MB, above the browser just didn't process the file - no feedback, didn't fire any event
Maybe it's a memory issue - tested same files with different machine with less memory and IE denied to work already at 5 MB files.

Is it possible to upload a text file to input in HTML/JS?

I have some input boxes in a HTML form that need to be updated when the form loads and these values need to be uploaded from a text file.
A similar question was also asked here:
Uploading Text File to Input in Html/JS
I have searched for this on the internet, but couldn't find any correct answer.
So I want to know whether it is possible or not?
If you wish to go the client side route, you'll be interested in the HTML5 FileReader API. Unfortunately, there is not wide browser support for this, so you may want to consider who will be using the functionality. Works in latest Chrome and Firefox, I think.
Here's a practical example: http://www.html5rocks.com/en/tutorials/file/dndfiles/#toc-reading-files
And I also read here to find the readAsText method: http://www.w3.org/TR/file-upload/#dfn-readAsText
I would do something like this (jQuery for brevity): http://jsfiddle.net/AjaDT/2/
Javascript
var fileInput = $('#files');
var uploadButton = $('#upload');
uploadButton.on('click', function() {
if (!window.FileReader) {
alert('Your browser is not supported');
return false;
}
var input = fileInput.get(0);
// Create a reader object
var reader = new FileReader();
if (input.files.length) {
var textFile = input.files[0];
// Read the file
reader.readAsText(textFile);
// When it's loaded, process it
$(reader).on('load', processFile);
} else {
alert('Please upload a file before continuing')
}
});
function processFile(e) {
var file = e.target.result,
results;
if (file && file.length) {
results = file.split("\n");
$('#name').val(results[0]);
$('#age').val(results[1]);
}
}
Text file
Jon
25
The other answer is great, but a bit outdated and it requires HTML & jQuery to run.
Here is how I do it, works in all modern browsers down to IE11.
/**
* Creates a file upload dialog and returns text in promise
* #returns {Promise<any>}
*/
function uploadText() {
return new Promise((resolve) => {
// create file input
const uploader = document.createElement('input')
uploader.type = 'file'
uploader.style.display = 'none'
// listen for files
uploader.addEventListener('change', () => {
const files = uploader.files
if (files.length) {
const reader = new FileReader()
reader.addEventListener('load', () => {
uploader.parentNode.removeChild(uploader)
resolve(reader.result)
})
reader.readAsText(files[0])
}
})
// trigger input
document.body.appendChild(uploader)
uploader.click()
})
}
// usage example
uploadText().then(text => {
console.log(text)
})
// async usage example
const text = await uploadText()

Is it possible in javascript to read local files in IE?

FileReader allows to read local file in Chrome.
function readMultipleFiles(evt) {
var files = evt.target.files;
if (files) {
for (var i = 0, f; f = files[i]; i++) {
var r = new FileReader();
r.onload = (function (f) {
return function (e) {
var contents = e.target.result;
document.getElementById("output").innerHTML = contents;
};
})(f);
r.readAsText(f);
}
} else {
alert("Failed to load files");
}
}
document.getElementById('fileinput').addEventListener('change', readMultipleFiles, false);​
Run example on jsfiddle.net
But this example does not work in Internet Explorer 9.
Does IE9 support File API?
If yes - what should I do to have ability to read local files in IE9?
You can use ActiveX' FileSystemObject.
var fso = new ActiveXObject("Scripting.FileSystemObject");
var a = fso.CreateTextFile("c:\\testfile.txt", true);
a.WriteLine("This is a test.");
a.Close();
If you make an hypertext application (.hta), you will be able to create the ActiveX object FileSystemObject, which will let you access the local file system.
Generally no. Reading local files is a massive security violation.
Does IE9 support File API?
No, it doesn't.
If yes - what should I do to have ability to read local files in IE9?
Did you mean if no? If so then you could use an ActiveX or just inform the user that this feature of your website is not supported on his browser and allow him the possibility to upload the file to the server.
Seems that FileSystemObject like a hidden monster..
var fso, file, fileName = '/file.txt', fileContents = '';
if (fileName) {
fso = new ActiveXObject('Scripting.FileSystemObject');
// don't worry about 'camelCase' typing, works both
if (fso.fileExists(fileName)) {
file = fso.openTextFile(fileName, 1);
fileContents = file.readAll();
// or loop over lines
// while (!file.atEndOfStream) {
// fileContents += file.readLine();
// }
file.close();
}
}
More info: https://msdn.microsoft.com/en-us/library/314cz14s(v=vs.85).aspx

Get Base64 encode file-data from Input Form

I've got a basic HTML form from which I can grab a bit of information that I'm examining in Firebug.
My only issues is that I'm trying to base64 encode the file data before it's sent to the server where it's required to be in that form to be saved to the database.
<input type="file" id="fileupload" />
And in Javascript+jQuery:
var file = $('#fileupload').attr("files")[0];
I have some operations based on available javascript: .getAsBinary(), .getAsText(), .getAsTextURL
However none of these return usable text that can be inserted as they contain unusable 'characters' - I don't want to have a 'postback' occur in my file uploaded, and I need to have multiple forms targeting specific objects so it's important I get the file and use Javascript this way.
How should I get the file in such a way that I can use one of the Javascript base64 encoders that are widely available!?
Thanks
Update - Starting bounty here, need cross-browser support!!!
Here is where I'm at:
<input type="file" id="fileuploadform" />
<script type="text/javascript">
var uploadformid = 'fileuploadform';
var uploadform = document.getElementById(uploadformid);
/* method to fetch and encode specific file here based on different browsers */
</script>
Couple of issues with cross browser support:
var file = $j(fileUpload.toString()).attr('files')[0];
fileBody = file.getAsDataURL(); // only would works in Firefox
Also, IE doesn't support:
var file = $j(fileUpload.toString()).attr('files')[0];
So I have to replace with:
var element = 'id';
var element = document.getElementById(id);
For IE Support.
This works in Firefox, Chrome and, Safari (but doesn't properly encode the file, or at least after it's been posted the file doesn't come out right)
var file = $j(fileUpload.toString()).attr('files')[0];
var encoded = Btoa(file);
Also,
file.readAsArrayBuffer()
Seems to be only supported in HTML5?
Lots of people suggested: http://www.webtoolkit.info/javascript-base64.html
But this only returns an error on the UTF_8 method before it base64 encodes? (or an empty string)
var encoded = Base64.encode(file);
It's entirely possible in browser-side javascript.
The easy way:
The readAsDataURL() method might already encode it as base64 for you. You'll probably need to strip out the beginning stuff (up to the first ,), but that's no biggie. This would take all the fun out though.
The hard way:
If you want to try it the hard way (or it doesn't work), look at readAsArrayBuffer(). This will give you a Uint8Array and you can use the method specified. This is probably only useful if you want to mess with the data itself, such as manipulating image data or doing other voodoo magic before you upload.
There are two methods:
Convert to string and use the built-in btoa or similar
I haven't tested all cases, but works for me- just get the char-codes
Convert directly from a Uint8Array to base64
I recently implemented tar in the browser. As part of that process, I made my own direct Uint8Array->base64 implementation. I don't think you'll need that, but it's here if you want to take a look; it's pretty neat.
What I do now:
The code for converting to string from a Uint8Array is pretty simple (where buf is a Uint8Array):
function uint8ToString(buf) {
var i, length, out = '';
for (i = 0, length = buf.length; i < length; i += 1) {
out += String.fromCharCode(buf[i]);
}
return out;
}
From there, just do:
var base64 = btoa(uint8ToString(yourUint8Array));
Base64 will now be a base64-encoded string, and it should upload just peachy. Try this if you want to double check before pushing:
window.open("data:application/octet-stream;base64," + base64);
This will download it as a file.
Other info:
To get the data as a Uint8Array, look at the MDN docs:
https://developer.mozilla.org/en/DOM/FileReader
My solution was use readAsBinaryString() and btoa() on its result.
uploadFileToServer(event) {
var file = event.srcElement.files[0];
console.log(file);
var reader = new FileReader();
reader.readAsBinaryString(file);
reader.onload = function() {
console.log(btoa(reader.result));
};
reader.onerror = function() {
console.log('there are some problems');
};
}
I used FileReader to display image on click of the file upload button not using any Ajax requests. Following is the code hope it might help some one.
$(document).ready(function($) {
$.extend( true, jQuery.fn, {
imagePreview: function( options ){
var defaults = {};
if( options ){
$.extend( true, defaults, options );
}
$.each( this, function(){
var $this = $( this );
$this.bind( 'change', function( evt ){
var files = evt.target.files; // FileList object
// Loop through the FileList and render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++) {
// Only process image files.
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
// Render thumbnail.
$('#imageURL').attr('src',e.target.result);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
});
});
}
});
$( '#fileinput' ).imagePreview();
});
Inspired by #Josef's answer:
const fileToBase64 = async (file) =>
new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => resolve(reader.result)
reader.onerror = (e) => reject(e)
})
const file = event.srcElement.files[0];
const imageStr = await fileToBase64(file)
Complete example
Html file input
<style>
.upload-button {
background-color: grey;
}
.upload-button input{
display:none;
}
</style>
<label for="upload-photo" class="upload-button">
Upload file
<input
type="file"
id="upload-photo"
</input>
</label>
JS Handler
document.getElementById("upload-photo").addEventListener("change", function({target}){
if (target.files && target.files.length) {
try {
const uploadedImageBase64 = await convertFileToBase64(target.files[0]);
//do something with above data string
} catch() {
//handle error
}
}
})
function convertFileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
// Typescript users: use following line
// reader.onload = () => resolve(reader.result as string);
reader.onerror = reject;
});
}
After struggling with this myself, I've come to implement FileReader for browsers that support it (Chrome, Firefox and the as-yet unreleased Safari 6), and a PHP script that echos back POSTed file data as Base64-encoded data for the other browsers.
So why dont you agree with user of the system to select an image from a known folder? Or they can set their choice folder for images.
Most browsers wont support full path but you can get the filename eg "image.png"
Using PHP inbuilt function to encode:
#$picture_base64 = base64_encode( file_get_contents($image_file_name) );
The sign # will suppress error if path is not found but the result will be a null for variable $picture_base64 so i guess youre ok with null like i am else do a check for null before proceeding.
In html you can select an image filename to the input e.g. "image.png" ( but not the full path)
<input type="file" name="image" id="image" >
Then in PHP you can do:
$path = "C:\\users\\john\\Desktop\\images\\"
#$picture_base64 = base64_encode( file_get_contents( $path. $_POST['image']);
Then $picture_base64 will be something like
"AQAAAAMAAAAHAAAADwAAAB8AAAA/AAAAfwAAAP8AAAD/AQAA/w"
I've started to think that using the 'iframe' for Ajax style upload might be a much better choice for my situation until HTML5 comes full circle and I don't have to support legacy browsers in my app!

Categories

Resources