Get Base64 encode file-data from Input Form
Asked Answered
F

8

119

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); 
Fluvial answered 8/8, 2011 at 5:43 Comment(5)
What are you uploading? Text data or binary?Jhelum
@tjamenson binary data -- anything really 'word document' 'pdf' 'image' etc. I was going to assign the filename in another variable but I was started to wonder if I had some fatal flaw in my understand of what is possible with upload forms and html -- is this method totally un-doable and it's not possible to transmit file data via posting it as a string? Also without HTML5 which I'm reading has some solutions -Fluvial
Interesting. Why are you base-64 encoding between browser and server, and trying to accomplish this in the browser? Seems like if your trying to secure the data, SSL would be much easier as it encrypts all of the HTTP data (including files) over the wire., and you could base-64 server side, if necessary for your database.Interference
@William I'm not trying to secure the data, just get it to the correct format. Salesforce.com (Apex platform) requires that database base64-encoded before the record is created in the database, their functionality to base64 encode file data via a form is very poorly documented (I figured out how to do this after asking this question). Anyways, it looks like base64-encoding binary file data is only supported natively in HTML5 or Mozilla File APIFluvial
OK, now I see why you need this. No intermediate server + crazy cloud requirements + no cross browser solution = 1 mess. Sorry.Interference
J
145

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:

Jhelum answered 8/8, 2011 at 8:14 Comment(5)
great -- I thought readAsArrayBuffer() output what looked like correct base64 data, but it wouldn't process because of the begging part of the string -- I'll give it a shot now!Fluvial
readAsArrayBuffer() doesn't seem to exist in Chrome/Safari and I'm assuming is going to be problematic in other browsers ~ is there an equivalent I can use in these other browsers?Fluvial
createObjectURL() <-- this looks promising I'll try to implement it, but I'm sure IE is going to need a 3rd wheelFluvial
Is it possible to assign a filename before execute your download solution?Exo
@Ωmega - No. All this does is download a binary stream. There's no way (AFAIK) to specify a filename. Some browsers guess an extension though. To get filenames, you'll have to upload then download again.Jhelum
Y
65

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');
    };
}
Yellowbird answered 7/3, 2017 at 11:26 Comment(2)
By far the easiest way, why doesn't this have more votes?Incitement
I still need to get value btoa(reader.result) to be assigned to variable to be attached to the form before submitting the form.Thirzia
F
15

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();
});
Flagellate answered 22/6, 2012 at 6:19 Comment(0)
M
9

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)
Matthieu answered 23/7, 2020 at 7:4 Comment(0)
E
5

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;
  });
}
Equation answered 20/4, 2021 at 6:12 Comment(3)
this is based on mb21's answer.Equation
the as keyword doesnt seem to work in javascript, i think you have put the answer in typescriptMuslin
@AbhishekMathur thanks, I have updated answer now! cheersEquation
P
1

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.

Psychopathology answered 3/4, 2012 at 14:49 Comment(0)
D
0

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"
Devoted answered 12/8, 2022 at 8:50 Comment(0)
F
-1

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!

Fluvial answered 20/8, 2011 at 17:39 Comment(2)
Just curious, why don't you just take [webtoolkit.info/javascript-base64.html#], and then comment-out input = Base64._utf8_encode(input); and output = Base64._utf8_decode(output);? Any problem other than utf-8 conversion error?Pas
@Pas I don't know how to grab the binary file data for input into these methods in each browser? $('#inputid').files[0] (doesn't work in IE7, as far as I can tell). If it were that easy? var output = Base64.encode($('#inputid').files[0]) ??Fluvial

© 2022 - 2024 — McMap. All rights reserved.