admin管理员组

文章数量:1024615

Before I start: This is a 100% client-side question. Forget the server side language, how I handle the uploads, etc -- I'm paring a known working HTTP file upload request with a jQuery-generated AJAX request that theoretically should be doing the same thing.

Background: I'm writing a file upload plugin for tinyMCE. Part of this involves allowing drag-and-drop image uploading for browsers that support it -- specifically, tinyMCE in firefox creates an img with a base64 src when a filesystem image is dropped into the tinyMCE editor. That's my current use case, it may be expanded later.

My goal is to take the base64 data and use jQuery to emulate a form submission to upload it to the server. I already have a normal HTML form-based approach that is working.

Getting the base64 data is cake:

$('img[src^="data:"]', ed.getDoc()).each(function(){
    var data = /data:(image\/\w+);base64,(.*)/gmi.exec(this.src), format, ext;
    if (data){
        format = data[1];
        ext = format.split('/')[1];
        data = atob(data[2]);
    }
    else{
        // blah, not supported
    }
});

Prepping the POST data similarly easy:

var boundary = '--------------------boundary' + (new Date).getTime();
data = '\r\n' + boundary + '\r\n' +
        'Content-Disposition: form-data; name="file-upload"; filename="uploaded_image.' + ext + '"\r\n' +
        'Content-Type: ' + format + '\r\n\r\n' +
        data + '\r\n' +
        boundary + '--'
        ;

All that's left is to send it off to the server:

$.ajax({
    type: 'POST',
    url: '/upload/',
    contentType: 'multipart/form-data; boundary=' + boundary.slice(2),
    data: data
});

The server handles the POST "correctly" (it sees the file and saves it to disk just fine), but the resulting image is quite corrupted -- it won't display in a browser, and its jpeg header is pletely wrong, not to mention it's about 33% larger than it is locally (12K on the server vs 9K local).

Using Firebug's Net tab, nothing looks wrong -- in fact, apart from an extra charset=UTF-8 in the Content-Type request header and the lack of pretty-printing POST data, this AJAX request looks exactly like the corresponding form POST. Using HttpFox, though, tells a different story:

form upload:

-----------------------------191891488320550623041315726177
Content-Disposition: form-data; name="file-upload"; filename="file.jpg"
Content-Type: image/jpeg

ÿØÿàJFIFHHÿÛC...

ajax upload:

--------------------boundary1375846064929
Content-Disposition: form-data; name="file-upload"; filename="file.jpeg"
Content-Type: image/jpeg

ÿÃÿà JFIFHHÿÃC...

I also notice that the request Content-Length values are different, again by about 33%. So it seems that, for whatever reason, jQuery-generated POST requests don't actually get sent in UTF-8? What am I missing, what's the final piece of this puzzle?

Before I start: This is a 100% client-side question. Forget the server side language, how I handle the uploads, etc -- I'm paring a known working HTTP file upload request with a jQuery-generated AJAX request that theoretically should be doing the same thing.

Background: I'm writing a file upload plugin for tinyMCE. Part of this involves allowing drag-and-drop image uploading for browsers that support it -- specifically, tinyMCE in firefox creates an img with a base64 src when a filesystem image is dropped into the tinyMCE editor. That's my current use case, it may be expanded later.

My goal is to take the base64 data and use jQuery to emulate a form submission to upload it to the server. I already have a normal HTML form-based approach that is working.

Getting the base64 data is cake:

$('img[src^="data:"]', ed.getDoc()).each(function(){
    var data = /data:(image\/\w+);base64,(.*)/gmi.exec(this.src), format, ext;
    if (data){
        format = data[1];
        ext = format.split('/')[1];
        data = atob(data[2]);
    }
    else{
        // blah, not supported
    }
});

Prepping the POST data similarly easy:

var boundary = '--------------------boundary' + (new Date).getTime();
data = '\r\n' + boundary + '\r\n' +
        'Content-Disposition: form-data; name="file-upload"; filename="uploaded_image.' + ext + '"\r\n' +
        'Content-Type: ' + format + '\r\n\r\n' +
        data + '\r\n' +
        boundary + '--'
        ;

All that's left is to send it off to the server:

$.ajax({
    type: 'POST',
    url: '/upload/',
    contentType: 'multipart/form-data; boundary=' + boundary.slice(2),
    data: data
});

The server handles the POST "correctly" (it sees the file and saves it to disk just fine), but the resulting image is quite corrupted -- it won't display in a browser, and its jpeg header is pletely wrong, not to mention it's about 33% larger than it is locally (12K on the server vs 9K local).

Using Firebug's Net tab, nothing looks wrong -- in fact, apart from an extra charset=UTF-8 in the Content-Type request header and the lack of pretty-printing POST data, this AJAX request looks exactly like the corresponding form POST. Using HttpFox, though, tells a different story:

form upload:

-----------------------------191891488320550623041315726177
Content-Disposition: form-data; name="file-upload"; filename="file.jpg"
Content-Type: image/jpeg

ÿØÿàJFIFHHÿÛC...

ajax upload:

--------------------boundary1375846064929
Content-Disposition: form-data; name="file-upload"; filename="file.jpeg"
Content-Type: image/jpeg

ÿÃÿà JFIFHHÿÃC...

I also notice that the request Content-Length values are different, again by about 33%. So it seems that, for whatever reason, jQuery-generated POST requests don't actually get sent in UTF-8? What am I missing, what's the final piece of this puzzle?

Share Improve this question edited Aug 7, 2013 at 3:54 eternicode asked Aug 7, 2013 at 3:43 eternicodeeternicode 6,9354 gold badges35 silver badges39 bronze badges 2
  • Could you please try after adding attribute processData:false in call to $.ajax. – closure Commented Aug 7, 2013 at 4:14
  • @closure, tried it, same behavior. – eternicode Commented Aug 7, 2013 at 14:15
Add a ment  | 

2 Answers 2

Reset to default 5

The solution turned out to be to use Typed Arrays and XHR2's FormData (polyfills are available for both, but both currently enjoy significant native support).

images.each(function(){
    var image = this,
        data = /data:(image\/\w+);base64,(.*)/gmi.exec(this.src),
        format, ext, binary;
    if (data){
        format = data[1];
        ext = format.split('/')[1];
        binary = atob(data[2]);
        data = new Uint8Array(binary.length);
        for (var i=0; i<binary.length; i++)
            data[i] = binary.charCodeAt(i);
    }
    else {
        // blah, not supported
    }

    var fd = new FormData();
    fd.append('file-upload', new Blob([data], {type: format}), 'uploaded_image.' + ext);

    $.ajax({
        type: 'POST',
        url: '/upload/',
        data: fd,
        processData: false,
        contentType: false
    });
});

You can also encode filename on client, then decode it on server.

var fd = new FormData();
fd.append('file', file.files[0], encodeURI(file.files[0].name));
$.ajax({
  type:"POST", 
  url:"/upload", 
  data : fd,
  contentType: false,
  processData: false
})

Server Side (Node.js with multer)

var storage = multer.diskStorage({
  destination: function (req, file, callback) {
    callback(null, './uploads');
  },
  filename: function (req, file, callback) {
    callback(null, decodeURI(file.originalname));
  }
});
router.post("/upload", upload.single("file"));

Before I start: This is a 100% client-side question. Forget the server side language, how I handle the uploads, etc -- I'm paring a known working HTTP file upload request with a jQuery-generated AJAX request that theoretically should be doing the same thing.

Background: I'm writing a file upload plugin for tinyMCE. Part of this involves allowing drag-and-drop image uploading for browsers that support it -- specifically, tinyMCE in firefox creates an img with a base64 src when a filesystem image is dropped into the tinyMCE editor. That's my current use case, it may be expanded later.

My goal is to take the base64 data and use jQuery to emulate a form submission to upload it to the server. I already have a normal HTML form-based approach that is working.

Getting the base64 data is cake:

$('img[src^="data:"]', ed.getDoc()).each(function(){
    var data = /data:(image\/\w+);base64,(.*)/gmi.exec(this.src), format, ext;
    if (data){
        format = data[1];
        ext = format.split('/')[1];
        data = atob(data[2]);
    }
    else{
        // blah, not supported
    }
});

Prepping the POST data similarly easy:

var boundary = '--------------------boundary' + (new Date).getTime();
data = '\r\n' + boundary + '\r\n' +
        'Content-Disposition: form-data; name="file-upload"; filename="uploaded_image.' + ext + '"\r\n' +
        'Content-Type: ' + format + '\r\n\r\n' +
        data + '\r\n' +
        boundary + '--'
        ;

All that's left is to send it off to the server:

$.ajax({
    type: 'POST',
    url: '/upload/',
    contentType: 'multipart/form-data; boundary=' + boundary.slice(2),
    data: data
});

The server handles the POST "correctly" (it sees the file and saves it to disk just fine), but the resulting image is quite corrupted -- it won't display in a browser, and its jpeg header is pletely wrong, not to mention it's about 33% larger than it is locally (12K on the server vs 9K local).

Using Firebug's Net tab, nothing looks wrong -- in fact, apart from an extra charset=UTF-8 in the Content-Type request header and the lack of pretty-printing POST data, this AJAX request looks exactly like the corresponding form POST. Using HttpFox, though, tells a different story:

form upload:

-----------------------------191891488320550623041315726177
Content-Disposition: form-data; name="file-upload"; filename="file.jpg"
Content-Type: image/jpeg

ÿØÿàJFIFHHÿÛC...

ajax upload:

--------------------boundary1375846064929
Content-Disposition: form-data; name="file-upload"; filename="file.jpeg"
Content-Type: image/jpeg

ÿÃÿà JFIFHHÿÃC...

I also notice that the request Content-Length values are different, again by about 33%. So it seems that, for whatever reason, jQuery-generated POST requests don't actually get sent in UTF-8? What am I missing, what's the final piece of this puzzle?

Before I start: This is a 100% client-side question. Forget the server side language, how I handle the uploads, etc -- I'm paring a known working HTTP file upload request with a jQuery-generated AJAX request that theoretically should be doing the same thing.

Background: I'm writing a file upload plugin for tinyMCE. Part of this involves allowing drag-and-drop image uploading for browsers that support it -- specifically, tinyMCE in firefox creates an img with a base64 src when a filesystem image is dropped into the tinyMCE editor. That's my current use case, it may be expanded later.

My goal is to take the base64 data and use jQuery to emulate a form submission to upload it to the server. I already have a normal HTML form-based approach that is working.

Getting the base64 data is cake:

$('img[src^="data:"]', ed.getDoc()).each(function(){
    var data = /data:(image\/\w+);base64,(.*)/gmi.exec(this.src), format, ext;
    if (data){
        format = data[1];
        ext = format.split('/')[1];
        data = atob(data[2]);
    }
    else{
        // blah, not supported
    }
});

Prepping the POST data similarly easy:

var boundary = '--------------------boundary' + (new Date).getTime();
data = '\r\n' + boundary + '\r\n' +
        'Content-Disposition: form-data; name="file-upload"; filename="uploaded_image.' + ext + '"\r\n' +
        'Content-Type: ' + format + '\r\n\r\n' +
        data + '\r\n' +
        boundary + '--'
        ;

All that's left is to send it off to the server:

$.ajax({
    type: 'POST',
    url: '/upload/',
    contentType: 'multipart/form-data; boundary=' + boundary.slice(2),
    data: data
});

The server handles the POST "correctly" (it sees the file and saves it to disk just fine), but the resulting image is quite corrupted -- it won't display in a browser, and its jpeg header is pletely wrong, not to mention it's about 33% larger than it is locally (12K on the server vs 9K local).

Using Firebug's Net tab, nothing looks wrong -- in fact, apart from an extra charset=UTF-8 in the Content-Type request header and the lack of pretty-printing POST data, this AJAX request looks exactly like the corresponding form POST. Using HttpFox, though, tells a different story:

form upload:

-----------------------------191891488320550623041315726177
Content-Disposition: form-data; name="file-upload"; filename="file.jpg"
Content-Type: image/jpeg

ÿØÿàJFIFHHÿÛC...

ajax upload:

--------------------boundary1375846064929
Content-Disposition: form-data; name="file-upload"; filename="file.jpeg"
Content-Type: image/jpeg

ÿÃÿà JFIFHHÿÃC...

I also notice that the request Content-Length values are different, again by about 33%. So it seems that, for whatever reason, jQuery-generated POST requests don't actually get sent in UTF-8? What am I missing, what's the final piece of this puzzle?

Share Improve this question edited Aug 7, 2013 at 3:54 eternicode asked Aug 7, 2013 at 3:43 eternicodeeternicode 6,9354 gold badges35 silver badges39 bronze badges 2
  • Could you please try after adding attribute processData:false in call to $.ajax. – closure Commented Aug 7, 2013 at 4:14
  • @closure, tried it, same behavior. – eternicode Commented Aug 7, 2013 at 14:15
Add a ment  | 

2 Answers 2

Reset to default 5

The solution turned out to be to use Typed Arrays and XHR2's FormData (polyfills are available for both, but both currently enjoy significant native support).

images.each(function(){
    var image = this,
        data = /data:(image\/\w+);base64,(.*)/gmi.exec(this.src),
        format, ext, binary;
    if (data){
        format = data[1];
        ext = format.split('/')[1];
        binary = atob(data[2]);
        data = new Uint8Array(binary.length);
        for (var i=0; i<binary.length; i++)
            data[i] = binary.charCodeAt(i);
    }
    else {
        // blah, not supported
    }

    var fd = new FormData();
    fd.append('file-upload', new Blob([data], {type: format}), 'uploaded_image.' + ext);

    $.ajax({
        type: 'POST',
        url: '/upload/',
        data: fd,
        processData: false,
        contentType: false
    });
});

You can also encode filename on client, then decode it on server.

var fd = new FormData();
fd.append('file', file.files[0], encodeURI(file.files[0].name));
$.ajax({
  type:"POST", 
  url:"/upload", 
  data : fd,
  contentType: false,
  processData: false
})

Server Side (Node.js with multer)

var storage = multer.diskStorage({
  destination: function (req, file, callback) {
    callback(null, './uploads');
  },
  filename: function (req, file, callback) {
    callback(null, decodeURI(file.originalname));
  }
});
router.post("/upload", upload.single("file"));

本文标签: javascriptjQueryajax emulating POST file uploadnot sending UTF8 contentStack Overflow