173 lines
5.0 KiB
JavaScript
173 lines
5.0 KiB
JavaScript
/**
|
||
* @fileOverview Transport
|
||
* @todo 支持chunked传输,优势:
|
||
* 可以将大文件分成小块,挨个传输,可以提高大文件成功率,当失败的时候,也只需要重传那小部分,
|
||
* 而不需要重头再传一次。另外断点续传也需要用chunked方式。
|
||
*/
|
||
define([
|
||
'../../base',
|
||
'./runtime'
|
||
], function( Base, Html5Runtime ) {
|
||
|
||
var noop = Base.noop,
|
||
$ = Base.$;
|
||
|
||
return Html5Runtime.register( 'Transport', {
|
||
init: function() {
|
||
this._status = 0;
|
||
this._response = null;
|
||
},
|
||
|
||
send: function() {
|
||
var owner = this.owner,
|
||
opts = this.options,
|
||
xhr = this._initAjax(),
|
||
blob = owner._blob,
|
||
server = opts.server,
|
||
formData, binary, fr;
|
||
|
||
if ( opts.sendAsBinary ) {
|
||
server += (/\?/.test( server ) ? '&' : '?') +
|
||
$.param( owner._formData );
|
||
|
||
binary = blob.getSource();
|
||
} else {
|
||
formData = new FormData();
|
||
$.each( owner._formData, function( k, v ) {
|
||
formData.append( k, v );
|
||
});
|
||
|
||
formData.append( opts.fileVal, blob.getSource(),
|
||
opts.filename || owner._formData.name || '' );
|
||
}
|
||
|
||
if ( opts.withCredentials && 'withCredentials' in xhr ) {
|
||
xhr.open( opts.method, server, true );
|
||
xhr.withCredentials = true;
|
||
} else {
|
||
xhr.open( opts.method, server );
|
||
}
|
||
|
||
this._setRequestHeader( xhr, opts.headers );
|
||
|
||
if ( binary ) {
|
||
// 强制设置成 content-type 为文件流。
|
||
xhr.overrideMimeType &&
|
||
xhr.overrideMimeType('application/octet-stream');
|
||
|
||
// android直接发送blob会导致服务端接收到的是空文件。
|
||
// bug详情。
|
||
// https://code.google.com/p/android/issues/detail?id=39882
|
||
// 所以先用fileReader读取出来再通过arraybuffer的方式发送。
|
||
if ( Base.os.android ) {
|
||
fr = new FileReader();
|
||
|
||
fr.onload = function() {
|
||
xhr.send( this.result );
|
||
fr = fr.onload = null;
|
||
};
|
||
|
||
fr.readAsArrayBuffer( binary );
|
||
} else {
|
||
xhr.send( binary );
|
||
}
|
||
} else {
|
||
xhr.send( formData );
|
||
}
|
||
},
|
||
|
||
getResponse: function() {
|
||
return this._response;
|
||
},
|
||
|
||
getResponseAsJson: function() {
|
||
return this._parseJson( this._response );
|
||
},
|
||
|
||
getStatus: function() {
|
||
return this._status;
|
||
},
|
||
|
||
abort: function() {
|
||
var xhr = this._xhr;
|
||
|
||
if ( xhr ) {
|
||
xhr.upload.onprogress = noop;
|
||
xhr.onreadystatechange = noop;
|
||
xhr.abort();
|
||
|
||
this._xhr = xhr = null;
|
||
}
|
||
},
|
||
|
||
destroy: function() {
|
||
this.abort();
|
||
},
|
||
|
||
_initAjax: function() {
|
||
var me = this,
|
||
xhr = new XMLHttpRequest(),
|
||
opts = this.options;
|
||
|
||
if ( opts.withCredentials && !('withCredentials' in xhr) &&
|
||
typeof XDomainRequest !== 'undefined' ) {
|
||
xhr = new XDomainRequest();
|
||
}
|
||
|
||
xhr.upload.onprogress = function( e ) {
|
||
var percentage = 0;
|
||
|
||
if ( e.lengthComputable ) {
|
||
percentage = e.loaded / e.total;
|
||
}
|
||
|
||
return me.trigger( 'progress', percentage );
|
||
};
|
||
|
||
xhr.onreadystatechange = function() {
|
||
|
||
if ( xhr.readyState !== 4 ) {
|
||
return;
|
||
}
|
||
|
||
xhr.upload.onprogress = noop;
|
||
xhr.onreadystatechange = noop;
|
||
me._xhr = null;
|
||
me._status = xhr.status;
|
||
|
||
if ( xhr.status >= 200 && xhr.status < 300 ) {
|
||
me._response = xhr.responseText;
|
||
return me.trigger('load');
|
||
} else if ( xhr.status >= 500 && xhr.status < 600 ) {
|
||
me._response = xhr.responseText;
|
||
return me.trigger( 'error', 'server-'+status );
|
||
}
|
||
|
||
|
||
return me.trigger( 'error', me._status ? 'http-'+status : 'abort' );
|
||
};
|
||
|
||
me._xhr = xhr;
|
||
return xhr;
|
||
},
|
||
|
||
_setRequestHeader: function( xhr, headers ) {
|
||
$.each( headers, function( key, val ) {
|
||
xhr.setRequestHeader( key, val );
|
||
});
|
||
},
|
||
|
||
_parseJson: function( str ) {
|
||
var json;
|
||
|
||
try {
|
||
json = JSON.parse( str );
|
||
} catch ( ex ) {
|
||
json = {};
|
||
}
|
||
|
||
return json;
|
||
}
|
||
});
|
||
});
|