-7

I'm using the following approach in order to preview images before uploading them:

$("#file").change(function() {
  var reader = new FileReader();
  reader.readAsArrayBuffer(this.files[0]);
  var fileName = this.files[0].name;
  var fileType = this.files[0].type;
  alert(fileType)
  reader.onloadend = function() {
    var base64Image = btoa(String.fromCharCode.apply(null, new Uint8Array(this.result)));
    // I show the image now and convert the data to base 64
  }
}

I have noticed that when the image is large, the method fails and I cannot preview the image.
I am unsure if the problem is due to base64 conversion or the FileReader.
Is there any setting to increase the max size, or is there any work around?

Here is the error message thrown in the console :

Uncaught RangeError: Maximum call stack size exceeded
at FileReader.reader.onloadend

Kaiido
  • 87,051
  • 7
  • 143
  • 194
Arnold Zahrneinder
  • 3,368
  • 4
  • 28
  • 54

4 Answers4

1

Your problem is that you use Function.apply which will convert your Typed Array items to arguments to the String.fromCharCode method.

Functions have a maximum arguments length limit.

To avoid this, when dealing with large files, the best way is to not process it at all.

If you need to send the file to your server, simply send the Blob directly, this can be easily achieved with the FormData API.

If you need to display the file i.e in HTML media element, then use URL.createObjectURL(yourFile) method.

And if you really need a dataURI version of the file, then use reader.readAsDataURL(yourFile) method.

Community
  • 1
  • 1
Kaiido
  • 87,051
  • 7
  • 143
  • 194
  • I'm sorry but you are not aware how terrible your approach is. check my own answer. – Arnold Zahrneinder Oct 30 '16 at 05:06
  • @Arrrr, and why ? Let me tell you that readAsBinaryString has been [deprecated](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsBinaryString)... And if you read carefully my answer, you'll see that **YOU SHOULD NOT PROCESS THE FILE** – Kaiido Oct 30 '16 at 05:07
  • It doesn't matter, but yours is deadly slow and somehow insane. – Arnold Zahrneinder Oct 30 '16 at 05:08
  • I solved my problem with causing minimum impact on the code and its structure in an effective way. But consider yours... – Arnold Zahrneinder Oct 30 '16 at 05:10
  • Yours is still slower than `reader.readAsDataURL` And using non standard and deprecated features is not what I call an "effective way" – Kaiido Oct 30 '16 at 05:10
  • reader.readAsDataURI causes problem when the file has to be decoded in the server side. Research you will see why. – Arnold Zahrneinder Oct 30 '16 at 05:11
  • It includes things that should not be there like the file format!! I guess you know this. And plus, this method may be deprecated BUT it is way way faster than your method. – Arnold Zahrneinder Oct 30 '16 at 05:14
  • My method is using the `FormData` API, you can't go faster, it doesn't do anything on the file, except transfering it (instead of a 30% larger string...) – Kaiido Oct 30 '16 at 05:16
  • FormData is incapable of serializing the file in a proper format :) Specially to `IFormFIle`. Do you know ASP.Net Core?? – Arnold Zahrneinder Oct 30 '16 at 05:18
  • @Arrrr, no I don't but I seriously doubt you can't access a file sent with a multipart POST request... A quick googling gave me that you should listen to `Request.Form.Files;` – Kaiido Oct 30 '16 at 05:21
  • No need to sneak into the request, there's a [FromBody] attribute which you can use :) and in my opinion Internet Explorer is dead, I really don't care about IE when developing website and I don't even target that thing. That method is supported by Edge, I just tested and worked fine. – Arnold Zahrneinder Oct 30 '16 at 05:23
  • 1
    No the problem with deprecated methods is not the current implementation, it is the future ones... And you are still doing useless processing of your file. But I see you're hard to get out from your silly biased opinions and I won't loose much more time with you. I simply hope someone which is open to learn something will read this answer in the future. – Kaiido Oct 30 '16 at 05:27
  • The processing is for 2 purposes: 1- Showing the preview of the file, 2: adding the base64 version of the file in a JSON model. Why?? because I can include many images in a JSON model and effortlessly deserialize them in the server side. Why?? because the file upload is implemented using a web api. Why JSON models, because the body must be deserialized, because the non-existent HttpPostedFileBase or the existing IFromFile are not known to JS. – Arnold Zahrneinder Oct 30 '16 at 05:30
  • For 1 use URL.createObjectURL. For 2 you seem to have a real flow in your server-side code, but I'll leave it to you... I did answer your question : the problem you were facing was caused by `apply`, not by the FileReader. I also gave you the best ways to do what you are trying to achieve. You don't want to try it, well I can't do much more for you. GoodBye. – Kaiido Oct 30 '16 at 05:33
  • Kaiido, first learn some server side programming, then come here and we discuss why this way is good. – Arnold Zahrneinder Oct 30 '16 at 05:37
  • Your question is about a FrontSide problem. Read it again. The only place you talk about server-side is in your insulting comments. I gave you an answer, if you then have a problem on how to handle a file upload with ASP.net, then ask a new question (but I'm sure there are already a lot of dupes). – Kaiido Oct 30 '16 at 05:39
  • Well. I asked the question peacefully, if you read the comment you see the other people started to attack, they were of no help of course. And your approach was surely not the best way! – Arnold Zahrneinder Oct 30 '16 at 05:45
  • 2
    What I see when reading these comments is you taking more than 20 minutes to understand that **we** need to read the exact message reported by your console. I couldn't have found your real issue without this. You can't ask us to try to rebuild your app in order to get ourselves the error you have. – Kaiido Oct 30 '16 at 05:50
  • 2
    @Arrrr: There’s a certain irony in telling someone to learn server-side programming when you’re sending an image to the server base64-encoded. Use multipart/form-data for efficient transport. Yes, this is the best way. – Ry- Oct 30 '16 at 08:35
  • @Ryan: I want the file(s) to be part of my request model. Anyways, are you sure that I can preview the image using form-data?? Your viewpoint is limited, base64 in terms of size is larger than binary data, which means if someone is specifically interested in transiting binary data in base64, there must be a reason. I'm neither dealing with a `Form` nor a simple uploader, but you are thinking so simply that you see the irony :) – Arnold Zahrneinder Oct 30 '16 at 12:16
  • @Arrrr: Kalido already told you how to preview, too: using `URL.createObjectURL`. There is no reason you have to use the same tool for both. – Ry- Oct 30 '16 at 12:33
  • @Ryan: Then what happens to the data that must be part of the request model?? – Arnold Zahrneinder Oct 30 '16 at 12:35
  • @Ryan: What if you really wanna check if the file is an image and not just something with a fake extension? What if you wanna protect the server against dummy requests? What is the disadvantage of Base64 encoding the file After it passes all validations?? – Arnold Zahrneinder Oct 30 '16 at 12:37
  • 2
    @Arrrr: Okay, if you're trusting the client to confirm that something is actually an image, you're doing it *completely* wrong. The server has to check that. Anyone can change the client. base64 does not magically make this safer. – Ry- Oct 30 '16 at 12:38
  • @Ryan: I don't trust the client and that is why I wanna check the file :) I wanna implement my own secure mechanism. I don't want any request not meeting my criteria to be even posted. I need to know what file that is first, you see gmail blocks executables after you upload them, that is annoying, I wanna prevent upload if needed beforehand. – Arnold Zahrneinder Oct 30 '16 at 12:42
  • @Ryan: "Anyone can change the client" not if it is protected against side-scripting. – Arnold Zahrneinder Oct 30 '16 at 12:42
  • *I wanna prevent upload if needed beforehand* - okay, but base64 doesn't help you with this? – Ry- Oct 30 '16 at 12:54
  • @Ryan:Yes Base64 has nothing to do with security you are so right, but reading the file in the client side has something to with that. I need to have a strongly typed model of accepted files to send to my Web API. There will be no form involved so you cannot get the files out of the request. Furthermore, if you wanna write a generic file handler API, using the File array in the request you cannot understand which file is which. – Arnold Zahrneinder Oct 30 '16 at 13:01
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/127000/discussion-between-arrrr-and-ryan). – Arnold Zahrneinder Oct 30 '16 at 13:06
  • @Arrrr, if you want to do file checking client side, even if you'll have to do it server side too, then just check for the 4 first bytes of the file : http://stackoverflow.com/questions/18299806/how-to-check-file-mime-type-with-javascript-before-upload/29672957#29672957 – Kaiido Oct 30 '16 at 13:21
  • I explained the whole problem in the chat link, please check it. – Arnold Zahrneinder Oct 30 '16 at 13:25
0

If you read the file using the FileReader, the whole file will be loaded into the memory. If you'd like handle large files, this will simply result in your web browser crashing right away. If you are really interested in passing your file as a Base64 String, I recommend you to add file size constraints in order to prevent any potential problems. As a conclusion, none of the methods of the FileReader class would be suitable for this purpose unless and again unless you are dealing with small files not larger than 100MG or so, otherwise you will run into problems.

Transcendent
  • 4,866
  • 4
  • 19
  • 46
0

Works for me:

var reader = new FileReader();

reader.onload = function (evt) {
    var binary = '';
    var bytes = new Uint8Array(reader.result);
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode(bytes[i]);
    }
    console.log(btoa(binary))
}

reader.readAsArrayBuffer(file)
dibrovsd
  • 71
  • 4
-6

After playing around here's the solution:

 $("#file").change(function () {
        var reader = new FileReader();
        reader.readAsBinaryString(this.files[0]);
        var fileName = this.files[0].name;
        var fileType = this.files[0].type;
        alert(fileType)
        reader.onloadend = function () { 
             var base64Image = btoa(this.result);
        }
}
Arnold Zahrneinder
  • 3,368
  • 4
  • 28
  • 54
  • 4
    "*This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web*" https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsBinaryString – Kaiido Oct 30 '16 at 05:14