Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPEG data obtained from FileReader doesn't match data in file

I'm trying to select a local JPEG file in the web browser via the HTML5 FileReader so I can submit it to a server without reloading the page. All the mechanics are working and I think I'm transferring and saving the exact data that JavaScript gave me, but the result is an invalid JPEG file on the server. Here's the basic code that demonstrates the problem:

<form name="add_photos">
    ​<input type=​"file" name=​"photo" id=​"photo" /><br />
    ​<input type=​"button" value=​"Upload" onclick=​"upload_photo()​;​" />​
</form>

<script type="text/javascript">
    function upload_photo() {
        file = document.add_photos.photo.files[0];
        if (file) {
            fileReader = new FileReader();
            fileReader.onload = upload_photo_ready;
            fileReader.readAsBinaryString(file);
        }
    }

    function upload_photo_ready(event) {
        data = event.target.result;
        // alert(data);

        URL = "submit.php";
        ajax = new XMLHttpRequest();
        ajax.open("POST", URL, 1);
        ajax.setRequestHeader("Ajax-Request", "1");
        ajax.send(data);
    }
</script>

Then my PHP script does this:

$data = file_get_contents("php://input");
$filename = "test.jpg";
file_put_contents($filename, $data);
$result = imagecreatefromjpeg($filename);

That last line throws a PHP error "test.jpg is not a valid JPEG file." If I download the data back to my Mac and try to open it in Preview, Preview says the file "may be damaged or use a file format that Preview doesn’t recognize."

If I open both the original file on my desktop and the uploaded file on the server in text editors to inspect their contents, they are almost but not quite the same. The original file starts like this:

ˇÿˇ‡JFIFˇ˛;CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 90

But the uploaded file starts like this:

ÿØÿàJFIFÿþ;CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 90

Interestingly, if I view the data in a JavaScript alert with the commented-out line above, it looks just like the uploaded file's data, so it seems as if the FileReader isn't giving the correct data at the very beginning, as opposed to a problem that is introduced while transferring or saving the data on the server. Can anyone explain this?

I'm using Safari 6 and I also tried Firefox 14.

UPDATE: I just figured out that if I skip the FileReader code and change ajax.send(data) to ajax.send(file), the image is transferred and saved correctly on the server. So my problem is basically solved, but I'll award the answer points to anyone who can explain why my original approach with readAsBinaryString didn't work.

like image 450
arlomedia Avatar asked Mar 22 '13 10:03

arlomedia


1 Answers

Your problem lies with readAsBinaryString. This will transfer the binary data byte-for-byte into a string, so that you will send a text string to your PHP file. Now a text string always has an encoding; and when you use XmlHttpRequest to upload a string, by default it will use UTF-8.

So each character, which was originally supposed to represent one byte, will be encoded as UTF-8... which uses multiple bytes for each character with a code point above 127!

Your best best is to use readAsArrayBuffer instead of readAsBinaryString. This will avoid all the character set conversions (that are necessary when dealing with strings).

like image 200
Martijn Avatar answered Oct 19 '22 23:10

Martijn