param { string } action param { object } option param { object } xhr return { object } function getError ( action , _option , xhr ) { let msg ; if ( xhr . response ) { msg = `${ xhr . status } ${ xhr . response . error || xhr . response }` ; } else if ( xhr . responseText ) { msg = `${ xhr . status } ${ xhr . responseText }` ; } else { msg = `fail to post ${ action } ${ xhr . status }` ; } const err = new Error ( msg ) as any ; err . status = xhr . status ; err . method = 'post' ; err . url = action ; return err ; } param { object } xhr return { string } function getBody ( xhr ) { const text = xhr . responseText || xhr . response ; if ( ! text ) { return text ; } try { return JSON . parse ( text ) ; } catch ( e ) { return text ; } } param { object } option return { object } export default function upload ( option ) { if ( typeof XMLHttpRequest === 'undefined' ) { return ; } const xhr = new XMLHttpRequest ( ) ; const action = option . action ; if ( xhr . upload ) { xhr . upload . onprogress = function progress ( e ) { if ( e . total > 0 ) { e . percent = ( e . loaded / e . total ) * 100 ; } option . onProgress ( e ) ; } ; } const formData = new FormData ( ) ; if ( option . data ) { Object . keys ( option . data ) . forEach ( key => { formData . append ( key , option . data [ key ] ) ; } ) ; } formData . append ( option . filename , option . file ) ; xhr . onerror = function error ( e ) { option . onError ( e ) ; } ; xhr . onload = function onload ( ) { if ( xhr . status < 200 || xhr . status >= 300 ) { return option . onError ( getError ( action , option , xhr ) ) ; } option . onSuccess ( getBody ( xhr ) ) ; } ; xhr . open ( 'post' , action , true ) ; if ( option . withCredentials && 'withCredentials' in xhr ) { xhr . withCredentials = true ; } const headers = option . headers || { } ; for ( const item in headers ) { if ( headers . hasOwnProperty ( item ) && headers [ item ] !== null ) { xhr . setRequestHeader ( item , headers [ item ] ) ; } } xhr . send ( formData ) ; return xhr ; }