最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - jQuery.ajax emulating POST file upload, not sending UTF-8 content - Stack Overflow

programmeradmin2浏览0评论

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"));
发布评论

评论列表(0)

  1. 暂无评论