Related
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);
}
I have this in my Angular.js controller that downloads a CSV file:
var blob = new Blob([csvContent.join('')], { type: 'text/csv;charset=utf-8'});
var link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
link.href = URL.createObjectURL(blob);
link.download = 'teams.csv';
link.click();
This works perfectly in Chrome but not in IE. A browser console log says:
HTML7007: One or more blob URLs were revoked by closing the blob for
which they were created. These URLs will no longer resolve as the data
backing the URL has been freed.
What does it mean and how can I fix it?
Try this using, this or useragent
if (navigator.appVersion.toString().indexOf('.NET') > 0)
window.navigator.msSaveBlob(blob, filename);
else
{
var blob = new Blob(['stringhere'], { type: 'text/csv;charset=utf-8' });
var link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
link.href = URL.createObjectURL(blob);
link.download = 'teams.csv';
link.click();
}
IE won't allow you to open blobs directly. You have to use msSaveOrOpenBlob. There's also msSaveBlob
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else {
var objectUrl = URL.createObjectURL(blob);
window.open(objectUrl);
}
I needed to use a Blob to download a converted a base64 PNG image. I was able to successfully download the blob on IE11 with window.navigator.msSaveBlob
See the following msdn link:
http://msdn.microsoft.com/en-us/library/hh779016(v=vs.85).aspx
Specifically, you should call:
window.navigator.msSaveBlob(blobObject, 'msSaveBlob_testFile.txt');
where blobObject is a Blob created in the usual fashion.
Complete Solution for Chrome, Internet Explorer Firefox and Opera
There are lots of nice bits on this page, but I had to use a combination of a few things to get it all to work. Hopefully this helps you.
Use a button or link to trigger a function called download():
<button class="button-no save-btn" ng-click="download()">DOWNLOAD</button>
Put this in your controller:
$scope.download = function () {
// example shows a JSON file
var content = JSON.stringify($scope.stuffToPutInFile, null, " ");
var blob = new Blob([content], {type: 'application/json;charset=utf-8'});
if (window.navigator && window.navigator.msSaveBlob) {
// Internet Explorer workaround
$log.warn("Triggering download using msSaveBlob");
window.navigator.msSaveBlob(blob, "export.json");
} else {
// other browsers
$log.warn("Triggering download using webkit");
var url = (window.URL || window.webkitURL).createObjectURL(blob);
// create invisible element
var downloadLink = angular.element('<a></a>');
downloadLink.attr('href', url);
downloadLink.attr('download', 'export.json');
// make link invisible and add to the DOM (Firefox)
downloadLink.attr('style','display:none');
angular.element(document.body).append(downloadLink);
// trigger click
downloadLink[0].click();
}
};
What's your IE browser version? You need a modern browser or IE10+
http://caniuse.com/bloburls
Maybe you need some delay. What about with:
link.click();
setTimeout(function(){
document.body.createElementNS('http://www.w3.org/1999/xhtml', 'a');
URL.revokeObjectURL(link.href);
}, 100);
I needed to get the download feature to work in Chrome and IE11. I had good success with this code.
HTML
<div ng-repeat="attachment in attachments">
<a ng-click="openAttachment(attachment)" ng-href="{{attachment.fileRef}}">{{attachment.filename}}</a>
</div>
JS
$scope.openAttachment = function (attachment) {
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(
b64toBlob(attachment.attachment, attachment.mimeType),
attachment.filename
);
}
};
Done it this way, working fine for me.
downloadFile(data) {
if (navigator.msSaveBlob) {
let blob = new Blob([data], {
"type": "text/csv;charset=utf8;"
});
navigator.msSaveBlob(blob, this.fileName);
}
else {
let blob = new Blob(['\ufeff' + data], { type: 'text/csv;charset=utf-8;' });
let $link = document.createElement("a");
let url = URL.createObjectURL(blob);
$link.setAttribute("target", "_blank");
$link.setAttribute("href", url);
$link.setAttribute("download", this.fileName);
$link.style.visibility = "hidden";
document.body.appendChild($link);
$link.click();
document.body.removeChild($link);
}
}
Try to use this instead :
var blob = file.slice(0, file.size);
Create polyfill method as below,had a variable filename since in my case download filename was static.This method will be called while blob function is not supported as in case of Internet explorer
if (!HTMLCanvasElement.prototype.toBlob) {
Object.defineProperty(HTMLCanvasElement.prototype,
'toBlob', {
value: function (callback, type, quality) {
var canvas = this;
setTimeout(function () {
var binStr = atob(canvas.toDataURL(type, quality).split(',')[1]),
len = binStr.length,
arr = new Uint8Array(len);
for (var i = 0; i < len; i++) {
arr[i] = binStr.charCodeAt(i);
}
var blob = new Blob([arr], {
type: 'image/png'
});
window.navigator.msSaveOrOpenBlob(blob, fileName);
});
}
});
}
try {
const blob = new Blob([res.body], {
type: res.headers.get('Content-Type'),
});
const file = new File([blob], this.getFileName(res), {
type: res.headers.get('Content-Type'),
});
saveAs(file);
} catch (err) {
var textFileAsBlob = new Blob([res.body], {
type: res.headers.get('Content-Type'),
});
window.navigator.msSaveBlob(textFileAsBlob, this.getFileName(res));
}
To get the file name. Use the below function.
getFileName(response: any) {
let name: string;
try {
const contentDisposition: string = response.headers.get(
'content-disposition'
);
const [, filename] = contentDisposition.split('filename=');
name = filename;
} catch (e) {
name = 'File_Name_Not_Specified_' + new Date();
}
return name;
}
This worked for me.
I am using JQuery blob to export the JQuery array to CSV.
It is working on every browser except on Safari 5.1.7.
Safari browser on Windows 7.
I came to Know Blob has compatibility issues with Safari.
please let me know if there is any work around to achieve it.
Below is the code:
var usersCSVData = [];
usersCSVData.push('LastName ','FirstName ', 'Login ','City ','State','Location ');
var fileName = "UserCSVdata.csv";
var buffer = usersCSVData.join("\n");
var blob = new Blob([buffer], {
"type": "text/csv;charset=utf8;"
});
if (navigator.msSaveBlob) { // IE 10+
navigator.msSaveBlob(blob, fileName);
}
else {
var link = document.createElement("a");
if (link.download !== undefined) {
var url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", fileName);
link.style = "visibility:hidden";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
I am getting error:
"'[object BlobConstructor]' is not a constructor (evaluating 'new Blob([buffer], {
"type": "text/csv;charset=utf8;"
})')"
Try to use BlobBuilder or WebKitBlobBuilder first. Also such using blob constructors/builders solve some problems in Android Stock Browser 4.4-:
//cross browser BlobBuilder constructor
var customBlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MSBlobBuilder || window.MozBlobBuilder;
//result
var blob;
//Data
var buffer = ['LastName ','FirstName ', 'Login ','City ','State','Location '].join("\n");
//Try to use WebKitBlobBuilder first, It also solves some issues for Android Stock Browser
if (window.WebKitBlobBuilder) {
blob = new WebKitBlobBuilder();
blob.append(buffer);
blob = blob.getBlob("text/csv");
} else if (window.Blob) {
blob = new Blob([buffer], { type : "text/csv" });
} else {
blob = new customBlobBuilder();
blob.append(buffer);
blob = blob.getBlob("text/csv");
}
console.log(blob);
I have this in my Angular.js controller that downloads a CSV file:
var blob = new Blob([csvContent.join('')], { type: 'text/csv;charset=utf-8'});
var link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
link.href = URL.createObjectURL(blob);
link.download = 'teams.csv';
link.click();
This works perfectly in Chrome but not in IE. A browser console log says:
HTML7007: One or more blob URLs were revoked by closing the blob for
which they were created. These URLs will no longer resolve as the data
backing the URL has been freed.
What does it mean and how can I fix it?
Try this using, this or useragent
if (navigator.appVersion.toString().indexOf('.NET') > 0)
window.navigator.msSaveBlob(blob, filename);
else
{
var blob = new Blob(['stringhere'], { type: 'text/csv;charset=utf-8' });
var link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
link.href = URL.createObjectURL(blob);
link.download = 'teams.csv';
link.click();
}
IE won't allow you to open blobs directly. You have to use msSaveOrOpenBlob. There's also msSaveBlob
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else {
var objectUrl = URL.createObjectURL(blob);
window.open(objectUrl);
}
I needed to use a Blob to download a converted a base64 PNG image. I was able to successfully download the blob on IE11 with window.navigator.msSaveBlob
See the following msdn link:
http://msdn.microsoft.com/en-us/library/hh779016(v=vs.85).aspx
Specifically, you should call:
window.navigator.msSaveBlob(blobObject, 'msSaveBlob_testFile.txt');
where blobObject is a Blob created in the usual fashion.
Complete Solution for Chrome, Internet Explorer Firefox and Opera
There are lots of nice bits on this page, but I had to use a combination of a few things to get it all to work. Hopefully this helps you.
Use a button or link to trigger a function called download():
<button class="button-no save-btn" ng-click="download()">DOWNLOAD</button>
Put this in your controller:
$scope.download = function () {
// example shows a JSON file
var content = JSON.stringify($scope.stuffToPutInFile, null, " ");
var blob = new Blob([content], {type: 'application/json;charset=utf-8'});
if (window.navigator && window.navigator.msSaveBlob) {
// Internet Explorer workaround
$log.warn("Triggering download using msSaveBlob");
window.navigator.msSaveBlob(blob, "export.json");
} else {
// other browsers
$log.warn("Triggering download using webkit");
var url = (window.URL || window.webkitURL).createObjectURL(blob);
// create invisible element
var downloadLink = angular.element('<a></a>');
downloadLink.attr('href', url);
downloadLink.attr('download', 'export.json');
// make link invisible and add to the DOM (Firefox)
downloadLink.attr('style','display:none');
angular.element(document.body).append(downloadLink);
// trigger click
downloadLink[0].click();
}
};
What's your IE browser version? You need a modern browser or IE10+
http://caniuse.com/bloburls
Maybe you need some delay. What about with:
link.click();
setTimeout(function(){
document.body.createElementNS('http://www.w3.org/1999/xhtml', 'a');
URL.revokeObjectURL(link.href);
}, 100);
I needed to get the download feature to work in Chrome and IE11. I had good success with this code.
HTML
<div ng-repeat="attachment in attachments">
<a ng-click="openAttachment(attachment)" ng-href="{{attachment.fileRef}}">{{attachment.filename}}</a>
</div>
JS
$scope.openAttachment = function (attachment) {
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(
b64toBlob(attachment.attachment, attachment.mimeType),
attachment.filename
);
}
};
Done it this way, working fine for me.
downloadFile(data) {
if (navigator.msSaveBlob) {
let blob = new Blob([data], {
"type": "text/csv;charset=utf8;"
});
navigator.msSaveBlob(blob, this.fileName);
}
else {
let blob = new Blob(['\ufeff' + data], { type: 'text/csv;charset=utf-8;' });
let $link = document.createElement("a");
let url = URL.createObjectURL(blob);
$link.setAttribute("target", "_blank");
$link.setAttribute("href", url);
$link.setAttribute("download", this.fileName);
$link.style.visibility = "hidden";
document.body.appendChild($link);
$link.click();
document.body.removeChild($link);
}
}
Try to use this instead :
var blob = file.slice(0, file.size);
Create polyfill method as below,had a variable filename since in my case download filename was static.This method will be called while blob function is not supported as in case of Internet explorer
if (!HTMLCanvasElement.prototype.toBlob) {
Object.defineProperty(HTMLCanvasElement.prototype,
'toBlob', {
value: function (callback, type, quality) {
var canvas = this;
setTimeout(function () {
var binStr = atob(canvas.toDataURL(type, quality).split(',')[1]),
len = binStr.length,
arr = new Uint8Array(len);
for (var i = 0; i < len; i++) {
arr[i] = binStr.charCodeAt(i);
}
var blob = new Blob([arr], {
type: 'image/png'
});
window.navigator.msSaveOrOpenBlob(blob, fileName);
});
}
});
}
try {
const blob = new Blob([res.body], {
type: res.headers.get('Content-Type'),
});
const file = new File([blob], this.getFileName(res), {
type: res.headers.get('Content-Type'),
});
saveAs(file);
} catch (err) {
var textFileAsBlob = new Blob([res.body], {
type: res.headers.get('Content-Type'),
});
window.navigator.msSaveBlob(textFileAsBlob, this.getFileName(res));
}
To get the file name. Use the below function.
getFileName(response: any) {
let name: string;
try {
const contentDisposition: string = response.headers.get(
'content-disposition'
);
const [, filename] = contentDisposition.split('filename=');
name = filename;
} catch (e) {
name = 'File_Name_Not_Specified_' + new Date();
}
return name;
}
This worked for me.
I'm trying to make an application that stores and retrieves Video files to and from IndexedDB. However, I am having issues while retrieving in Firefox and while storing in Chrome. I'll post the code:
(function () {
// IndexedDB
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB,
IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.OIDBTransaction || window.msIDBTransaction,
dbVersion = 1.0;
// Create/open database
var request = indexedDB.open("videoFiles", dbVersion);
var db;
var createObjectStore = function (dataBase) {
// Create an objectStore
console.log("Creating objectStore")
dataBase.createObjectStore("earth");
},
getVideoFile = function () {
// Create XHR
var xhr = new XMLHttpRequest(),
blob;
xhr.open("GET", "day_the_earth_stood_still.ogv", true);
// Set the responseType to blob
xhr.responseType = "blob";
xhr.addEventListener("load", function () {
if (xhr.status === 200) {
console.log("Video retrieved");
// Blob as response
blob = xhr.response;
console.log("Blob:" + blob);
// Put the received blob into IndexedDB
putEarthInDb(blob);
}
}, false);
// Send XHR
xhr.send();
},
putEarthInDb = function (blob) {
console.log("Putting earth in IndexedDB");
// Open a transaction to the database
var transaction = db.transaction(["earth"], "readwrite");
// Put the blob into the dabase
var put = transaction.objectStore("earth").put(blob, "video");
// Retrieve the file that was just stored
transaction.objectStore("earth").get("video").onsuccess = function (event) {
var vidFile = event.target.result;
console.log("Got earth!" + vidFile);
console.log('File Type: ' + vidFile.type); /// THIS SHOWS : application/xml
// Get window.URL object
var URL = window.URL || window.webkitURL;
// Create and revoke ObjectURL
var vidURL = URL.createObjectURL(vidFile);
// Set vid src to ObjectURL
var vidEarth = document.getElementById("earth");
vidEarth.setAttribute("src", vidURL);
// Revoking ObjectURL
URL.revokeObjectURL(vidURL);
};
};
request.onerror = function (event) {
console.log("Error creating/accessing IndexedDB database");
};
request.onsuccess = function (event) {
console.log("Success creating/accessing IndexedDB database");
db = request.result;
db.onerror = function (event) {
console.log("Error creating/accessing IndexedDB database");
};
// Interim solution for Google Chrome to create an objectStore. Will be deprecated
if (db.setVersion) {
if (db.version != dbVersion) {
var setVersion = db.setVersion(dbVersion);
setVersion.onsuccess = function () {
createObjectStore(db);
getVideoFile();
};
}
else {
getVideoFile();
}
}
else {
getVideoFile();
}
}
// For future use. Currently only in latest Firefox versions
request.onupgradeneeded = function (event) {
createObjectStore(event.target.result);
};
})();
Problem 1(Firefox): In Firefox, the line console.log('File Type: ' + vidFile.type); above shows "application/xml" while GETTING a video file (mp4, ogv, webm) and so the Video tag says "video format or mime type is not supported".
However when I GET an image file like png it shows "image/png" and works well if the src of an img tag is set.
Problem 2(Chrome): In Chrome, both image and video are not even getting stored into the IndexedDB. At the following line:
var put = transaction.objectStore("earth").put(blob, "video");
Uncaught Error: DataCloneError: DOM IDBDatabase Exception 25 is thrown.
I am new to IndexedDB and have no clue on how to solve this. All I need to do is store video files into indexedDB, retrieve it and show in Video tag.
The HTML is shown below:
(mp4):
<div class="myVidDiv">
<video id="earth" type="video/mp4" codecs="avc1.42e01e, mp4a.40.2" controls> </video>
</div>
(ogv):
<div class="myVidDiv">
<video id="earth" type="video/ogg" codecs="theora, vorbis" controls></video>
</div>
Also tried without "codecs" attribute. Nothing works. I've been stuck with this for dayss together... Couldn't find any working example via google as well. Someone kindly help me with this.
Ok, I'll try to sum up what it came out from the comments.
1. Firefox
It seems that, originally, the Blob object you get from the AJAX request has content type application/xml, because that's what you get in response from the server. It can be a problem of misconfiguration.
If you have access to the HTTP server's configuration, it may be solved quite easily. If it's Apache, you can simply add this line:
AddType video/ogg .ogv
Save, restart Apache and you should be ok. If you can't change the server's configuration, you'll have to change the Blob's content type in order to match the desired one:
blob = xhr.response.slice(0, xhr.response.size, "video/ogg");
Note that this could be memory expensive because you're making a copy of a (probably) large file, but xhr.response should be sent to garbage after a couple of steps.
2. Chrome
It seems that Chrome still doesn't support Blob and File storing.
It also seems that they've already fixed the problem, but haven't deployed the fix yet. I wonder what they're waiting for :[
UPDATE: as of July 1st, 2014, Chrome dev supports storing blobs into IndexedDB. It's expected to land soon on the stable channel.