add vc-upload
parent
ed664a06c3
commit
6f3abfce42
|
@ -0,0 +1,3 @@
|
||||||
|
import upload from './src'
|
||||||
|
|
||||||
|
export default upload
|
|
@ -0,0 +1,200 @@
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
import BaseMixin from '../../_util/BaseMixin'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
import defaultRequest from './request'
|
||||||
|
import getUid from './uid'
|
||||||
|
import attrAccept from './attr-accept'
|
||||||
|
|
||||||
|
const upLoadPropTypes = {
|
||||||
|
component: PropTypes.string,
|
||||||
|
// style: PropTypes.object,
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
// className: PropTypes.string,
|
||||||
|
multiple: PropTypes.bool,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
accept: PropTypes.string,
|
||||||
|
// children: PropTypes.any,
|
||||||
|
// onStart: PropTypes.func,
|
||||||
|
data: PropTypes.oneOfType([
|
||||||
|
PropTypes.object,
|
||||||
|
PropTypes.func,
|
||||||
|
]),
|
||||||
|
headers: PropTypes.object,
|
||||||
|
beforeUpload: PropTypes.func,
|
||||||
|
customRequest: PropTypes.func,
|
||||||
|
// onProgress: PropTypes.func,
|
||||||
|
withCredentials: PropTypes.bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
const AjaxUploader = {
|
||||||
|
mixins: [BaseMixin],
|
||||||
|
props: upLoadPropTypes,
|
||||||
|
data () {
|
||||||
|
this.reqs = {}
|
||||||
|
return {
|
||||||
|
uid: getUid(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onChange (e) {
|
||||||
|
const files = e.target.files
|
||||||
|
this.uploadFiles(files)
|
||||||
|
this.reset()
|
||||||
|
},
|
||||||
|
onClick () {
|
||||||
|
const el = this.$refs.fileInputRef
|
||||||
|
if (!el) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
el.click()
|
||||||
|
},
|
||||||
|
onKeyDown (e) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.onClick()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFileDrop (e) {
|
||||||
|
if (e.type === 'dragover') {
|
||||||
|
e.preventDefault()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const files = Array.prototype.slice.call(e.dataTransfer.files).filter(
|
||||||
|
file => attrAccept(file, this.accept)
|
||||||
|
)
|
||||||
|
this.uploadFiles(files)
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
},
|
||||||
|
uploadFiles (files) {
|
||||||
|
const postFiles = Array.prototype.slice.call(files)
|
||||||
|
postFiles.forEach((file) => {
|
||||||
|
file.uid = getUid()
|
||||||
|
this.upload(file, postFiles)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
upload (file, fileList) {
|
||||||
|
if (!this.beforeUpload) {
|
||||||
|
// always async in case use react state to keep fileList
|
||||||
|
return setTimeout(() => this.post(file), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const before = this.beforeUpload(file, fileList)
|
||||||
|
if (before && before.then) {
|
||||||
|
before.then((processedFile) => {
|
||||||
|
const processedFileType = Object.prototype.toString.call(processedFile)
|
||||||
|
if (processedFileType === '[object File]' || processedFileType === '[object Blob]') {
|
||||||
|
this.post(processedFile)
|
||||||
|
} else {
|
||||||
|
this.post(file)
|
||||||
|
}
|
||||||
|
}).catch(e => {
|
||||||
|
console && console.log(e); // eslint-disable-line
|
||||||
|
})
|
||||||
|
} else if (before !== false) {
|
||||||
|
setTimeout(() => this.post(file), 0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
post (file) {
|
||||||
|
if (!this._isMounted) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let { data } = this.$props
|
||||||
|
if (typeof data === 'function') {
|
||||||
|
data = data(file)
|
||||||
|
}
|
||||||
|
const { uid } = file
|
||||||
|
const request = this.customRequest || defaultRequest
|
||||||
|
this.reqs[uid] = request({
|
||||||
|
action: this.action,
|
||||||
|
filename: this.name,
|
||||||
|
file,
|
||||||
|
data,
|
||||||
|
headers: this.headers,
|
||||||
|
withCredentials: this.withCredentials,
|
||||||
|
onProgress: e => {
|
||||||
|
this.$emit('progress', e, file)
|
||||||
|
},
|
||||||
|
onSuccess: (ret, xhr) => {
|
||||||
|
delete this.reqs[uid]
|
||||||
|
this.$emit('success', ret, file, xhr)
|
||||||
|
},
|
||||||
|
onError: (err, ret) => {
|
||||||
|
delete this.reqs[uid]
|
||||||
|
this.$emit('error', err, ret, file)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
this.$emit('start', file)
|
||||||
|
},
|
||||||
|
reset () {
|
||||||
|
this.setState({
|
||||||
|
uid: getUid(),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
abort (file) {
|
||||||
|
const { reqs } = this
|
||||||
|
if (file) {
|
||||||
|
let uid = file
|
||||||
|
if (file && file.uid) {
|
||||||
|
uid = file.uid
|
||||||
|
}
|
||||||
|
if (reqs[uid]) {
|
||||||
|
reqs[uid].abort()
|
||||||
|
delete reqs[uid]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Object.keys(reqs).forEach((uid) => {
|
||||||
|
if (reqs[uid]) {
|
||||||
|
reqs[uid].abort()
|
||||||
|
}
|
||||||
|
|
||||||
|
delete reqs[uid]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this._isMounted = true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
this._isMounted = false
|
||||||
|
this.abort()
|
||||||
|
},
|
||||||
|
render () {
|
||||||
|
const {
|
||||||
|
component: Tag, prefixCls, disabled, multiple, accept,
|
||||||
|
} = this.$props
|
||||||
|
const cls = classNames({
|
||||||
|
[prefixCls]: true,
|
||||||
|
[`${prefixCls}-disabled`]: disabled,
|
||||||
|
})
|
||||||
|
const events = disabled ? {} : {
|
||||||
|
onClick: this.onClick,
|
||||||
|
onKeydown: this.onKeyDown,
|
||||||
|
onDrop: this.onFileDrop,
|
||||||
|
onDragover: this.onFileDrop,
|
||||||
|
tabIndex: '0',
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Tag
|
||||||
|
{...events}
|
||||||
|
class={cls}
|
||||||
|
role='button'
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type='file'
|
||||||
|
ref='fileInputRef'
|
||||||
|
key={this.uid}
|
||||||
|
style={{ display: 'none' }}
|
||||||
|
accept={accept}
|
||||||
|
multiple={multiple}
|
||||||
|
onChange={this.onChange}
|
||||||
|
/>
|
||||||
|
{this.$slots.default}
|
||||||
|
</Tag>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AjaxUploader
|
|
@ -0,0 +1,272 @@
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
import BaseMixin from '../../_util/BaseMixin'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
import getUid from './uid'
|
||||||
|
import warning from '../../_util/warning'
|
||||||
|
|
||||||
|
const IFRAME_STYLE = {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
opacity: 0,
|
||||||
|
filter: 'alpha(opacity=0)',
|
||||||
|
left: 0,
|
||||||
|
zIndex: 9999,
|
||||||
|
}
|
||||||
|
|
||||||
|
// diferent from AjaxUpload, can only upload on at one time, serial seriously
|
||||||
|
const IframeUploader = {
|
||||||
|
mixins: [BaseMixin],
|
||||||
|
props: {
|
||||||
|
component: PropTypes.string,
|
||||||
|
// style: PropTypes.object,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
// className: PropTypes.string,
|
||||||
|
accept: PropTypes.string,
|
||||||
|
// onStart: PropTypes.func,
|
||||||
|
multiple: PropTypes.bool,
|
||||||
|
// children: PropTypes.any,
|
||||||
|
data: PropTypes.oneOfType([
|
||||||
|
PropTypes.object,
|
||||||
|
PropTypes.func,
|
||||||
|
]),
|
||||||
|
action: PropTypes.string,
|
||||||
|
name: PropTypes.string,
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
this.file = {}
|
||||||
|
return {
|
||||||
|
uploading: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onLoad () {
|
||||||
|
if (!this.uploading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const { file } = this
|
||||||
|
let response
|
||||||
|
try {
|
||||||
|
const doc = this.getIframeDocument()
|
||||||
|
const script = doc.getElementsByTagName('script')[0]
|
||||||
|
if (script && script.parentNode === doc.body) {
|
||||||
|
doc.body.removeChild(script)
|
||||||
|
}
|
||||||
|
response = doc.body.innerHTML
|
||||||
|
this.$emit('success', response, file)
|
||||||
|
} catch (err) {
|
||||||
|
warning(false, 'cross domain error for Upload. Maybe server should return document.domain script. see Note from https://github.com/react-component/upload')
|
||||||
|
response = 'cross-domain'
|
||||||
|
this.$emit('error', err, null, file)
|
||||||
|
}
|
||||||
|
this.endUpload()
|
||||||
|
},
|
||||||
|
onChange () {
|
||||||
|
const target = this.getFormInputNode()
|
||||||
|
// ie8/9 don't support FileList Object
|
||||||
|
// http://stackoverflow.com/questions/12830058/ie8-input-type-file-get-files
|
||||||
|
const file = this.file = {
|
||||||
|
uid: getUid(),
|
||||||
|
name: target.value,
|
||||||
|
}
|
||||||
|
this.startUpload()
|
||||||
|
const { $props: props } = this
|
||||||
|
if (!props.beforeUpload) {
|
||||||
|
return this.post(file)
|
||||||
|
}
|
||||||
|
const before = props.beforeUpload(file)
|
||||||
|
if (before && before.then) {
|
||||||
|
before.then(() => {
|
||||||
|
this.post(file)
|
||||||
|
}, () => {
|
||||||
|
this.endUpload()
|
||||||
|
})
|
||||||
|
} else if (before !== false) {
|
||||||
|
this.post(file)
|
||||||
|
} else {
|
||||||
|
this.endUpload()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getIframeNode () {
|
||||||
|
return this.$refs.iframeRef
|
||||||
|
},
|
||||||
|
getIframeDocument () {
|
||||||
|
return this.getIframeNode().contentDocument
|
||||||
|
},
|
||||||
|
getFormNode () {
|
||||||
|
return this.getIframeDocument().getElementById('form')
|
||||||
|
},
|
||||||
|
getFormInputNode () {
|
||||||
|
return this.getIframeDocument().getElementById('input')
|
||||||
|
},
|
||||||
|
getFormDataNode () {
|
||||||
|
return this.getIframeDocument().getElementById('data')
|
||||||
|
},
|
||||||
|
getFileForMultiple (file) {
|
||||||
|
return this.multiple ? [file] : file
|
||||||
|
},
|
||||||
|
getIframeHTML (domain) {
|
||||||
|
let domainScript = ''
|
||||||
|
let domainInput = ''
|
||||||
|
if (domain) {
|
||||||
|
const script = 'script'
|
||||||
|
domainScript = `<${script}>document.domain="${domain}";</${script}>`
|
||||||
|
domainInput = `<input name="_documentDomain" value="${domain}" />`
|
||||||
|
}
|
||||||
|
return `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<style>
|
||||||
|
body,html {padding:0;margin:0;border:0;overflow:hidden;}
|
||||||
|
</style>
|
||||||
|
${domainScript}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form method="post"
|
||||||
|
encType="multipart/form-data"
|
||||||
|
action="${this.action}" id="form"
|
||||||
|
style="display:block;height:9999px;position:relative;overflow:hidden;">
|
||||||
|
<input id="input" type="file"
|
||||||
|
name="${this.name}"
|
||||||
|
style="position:absolute;top:0;right:0;height:9999px;font-size:9999px;cursor:pointer;"/>
|
||||||
|
${domainInput}
|
||||||
|
<span id="data"></span>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
},
|
||||||
|
initIframeSrc () {
|
||||||
|
if (this.domain) {
|
||||||
|
this.getIframeNode().src = `javascript:void((function(){
|
||||||
|
var d = document;
|
||||||
|
d.open();
|
||||||
|
d.domain='${this.domain}';
|
||||||
|
d.write('');
|
||||||
|
d.close();
|
||||||
|
})())`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initIframe () {
|
||||||
|
const iframeNode = this.getIframeNode()
|
||||||
|
let win = iframeNode.contentWindow
|
||||||
|
let doc
|
||||||
|
this.domain = this.domain || ''
|
||||||
|
this.initIframeSrc()
|
||||||
|
try {
|
||||||
|
doc = win.document
|
||||||
|
} catch (e) {
|
||||||
|
this.domain = document.domain
|
||||||
|
this.initIframeSrc()
|
||||||
|
win = iframeNode.contentWindow
|
||||||
|
doc = win.document
|
||||||
|
}
|
||||||
|
doc.open('text/html', 'replace')
|
||||||
|
doc.write(this.getIframeHTML(this.domain))
|
||||||
|
doc.close()
|
||||||
|
this.getFormInputNode().onchange = this.onChange
|
||||||
|
},
|
||||||
|
endUpload () {
|
||||||
|
if (this.uploading) {
|
||||||
|
this.file = {}
|
||||||
|
// hack avoid batch
|
||||||
|
this.uploading = false
|
||||||
|
this.setState({
|
||||||
|
uploading: false,
|
||||||
|
})
|
||||||
|
this.initIframe()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
startUpload () {
|
||||||
|
if (!this.uploading) {
|
||||||
|
this.uploading = true
|
||||||
|
this.setState({
|
||||||
|
uploading: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateIframeWH () {
|
||||||
|
const rootNode = this.$el
|
||||||
|
const iframeNode = this.getIframeNode()
|
||||||
|
iframeNode.style.height = `${rootNode.offsetHeight}px`
|
||||||
|
iframeNode.style.width = `${rootNode.offsetWidth}px`
|
||||||
|
},
|
||||||
|
abort (file) {
|
||||||
|
if (file) {
|
||||||
|
let uid = file
|
||||||
|
if (file && file.uid) {
|
||||||
|
uid = file.uid
|
||||||
|
}
|
||||||
|
if (uid === this.file.uid) {
|
||||||
|
this.endUpload()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.endUpload()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
post (file) {
|
||||||
|
const formNode = this.getFormNode()
|
||||||
|
const dataSpan = this.getFormDataNode()
|
||||||
|
let { data } = this.$props
|
||||||
|
if (typeof data === 'function') {
|
||||||
|
data = data(file)
|
||||||
|
}
|
||||||
|
const inputs = document.createDocumentFragment()
|
||||||
|
for (const key in data) {
|
||||||
|
if (data.hasOwnProperty(key)) {
|
||||||
|
const input = document.createElement('input')
|
||||||
|
input.setAttribute('name', key)
|
||||||
|
input.value = data[key]
|
||||||
|
inputs.appendChild(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataSpan.appendChild(inputs)
|
||||||
|
formNode.submit()
|
||||||
|
dataSpan.innerHTML = ''
|
||||||
|
this.$emit('start', file)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.updateIframeWH()
|
||||||
|
this.initIframe()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updated () {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.updateIframeWH()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const {
|
||||||
|
component: Tag, disabled,
|
||||||
|
prefixCls,
|
||||||
|
} = this.$props
|
||||||
|
const iframeStyle = {
|
||||||
|
...IFRAME_STYLE,
|
||||||
|
display: this.uploading || disabled ? 'none' : '',
|
||||||
|
}
|
||||||
|
const cls = classNames({
|
||||||
|
[prefixCls]: true,
|
||||||
|
[`${prefixCls}-disabled`]: disabled,
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<Tag
|
||||||
|
className={cls}
|
||||||
|
style={{ position: 'relative', zIndex: 0 }}
|
||||||
|
>
|
||||||
|
<iframe
|
||||||
|
ref='iframeRef'
|
||||||
|
onLoad={this.onLoad}
|
||||||
|
style={iframeStyle}
|
||||||
|
/>
|
||||||
|
{this.$slots.default}
|
||||||
|
</Tag>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IframeUploader
|
|
@ -0,0 +1,91 @@
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
import { initDefaultProps } from '../../_util/props-util'
|
||||||
|
import BaseMixin from '../../_util/BaseMixin'
|
||||||
|
import AjaxUpload from './AjaxUploader'
|
||||||
|
import IframeUpload from './IframeUploader'
|
||||||
|
|
||||||
|
// function empty () {
|
||||||
|
// }
|
||||||
|
|
||||||
|
const uploadProps = {
|
||||||
|
component: PropTypes.string,
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
action: PropTypes.string,
|
||||||
|
name: PropTypes.string,
|
||||||
|
multipart: PropTypes.bool,
|
||||||
|
// onError: PropTypes.func,
|
||||||
|
// onSuccess: PropTypes.func,
|
||||||
|
// onProgress: PropTypes.func,
|
||||||
|
// onStart: PropTypes.func,
|
||||||
|
data: PropTypes.oneOfType([
|
||||||
|
PropTypes.object,
|
||||||
|
PropTypes.func,
|
||||||
|
]),
|
||||||
|
headers: PropTypes.object,
|
||||||
|
accept: PropTypes.string,
|
||||||
|
multiple: PropTypes.bool,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
beforeUpload: PropTypes.func,
|
||||||
|
customRequest: PropTypes.func,
|
||||||
|
// onReady: PropTypes.func,
|
||||||
|
withCredentials: PropTypes.bool,
|
||||||
|
supportServerRender: PropTypes.bool,
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
name: 'Upload',
|
||||||
|
mixins: [BaseMixin],
|
||||||
|
props: initDefaultProps(uploadProps, {
|
||||||
|
component: 'span',
|
||||||
|
prefixCls: 'rc-upload',
|
||||||
|
data: {},
|
||||||
|
headers: {},
|
||||||
|
name: 'file',
|
||||||
|
multipart: false,
|
||||||
|
// onReady: empty,
|
||||||
|
// onStart: empty,
|
||||||
|
// onError: empty,
|
||||||
|
// onSuccess: empty,
|
||||||
|
supportServerRender: false,
|
||||||
|
multiple: false,
|
||||||
|
beforeUpload: null,
|
||||||
|
customRequest: null,
|
||||||
|
withCredentials: false,
|
||||||
|
}),
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
Component: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.supportServerRender) {
|
||||||
|
/* eslint react/no-did-mount-set-state:0 */
|
||||||
|
this.setState({
|
||||||
|
Component: this.getComponent(),
|
||||||
|
}, () => {
|
||||||
|
this.$emit('ready')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getComponent () {
|
||||||
|
return typeof File !== 'undefined' ? AjaxUpload : IframeUpload
|
||||||
|
},
|
||||||
|
abort (file) {
|
||||||
|
this.$refs.uploaderRef.abort(file)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
if (this.supportServerRender) {
|
||||||
|
const ComponentUploader = this.Component
|
||||||
|
if (ComponentUploader) {
|
||||||
|
return <ComponentUploader {...this.$props} ref='uploaderRef' />
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const ComponentUploader = this.getComponent()
|
||||||
|
return <ComponentUploader {...this.$props} ref='uploaderRef' />
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
function endsWith (str, suffix) {
|
||||||
|
return str.indexOf(suffix, str.length - suffix.length) !== -1
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (file, acceptedFiles) => {
|
||||||
|
if (file && acceptedFiles) {
|
||||||
|
const acceptedFilesArray = Array.isArray(acceptedFiles)
|
||||||
|
? acceptedFiles
|
||||||
|
: acceptedFiles.split(',')
|
||||||
|
const fileName = file.name || ''
|
||||||
|
const mimeType = file.type || ''
|
||||||
|
const baseMimeType = mimeType.replace(/\/.*$/, '')
|
||||||
|
|
||||||
|
return acceptedFilesArray.some(type => {
|
||||||
|
const validType = type.trim()
|
||||||
|
if (validType.charAt(0) === '.') {
|
||||||
|
return endsWith(fileName.toLowerCase(), validType.toLowerCase())
|
||||||
|
} else if (/\/\*$/.test(validType)) {
|
||||||
|
// This is something like a image/* mime type
|
||||||
|
return baseMimeType === validType.replace(/\/.*$/, '')
|
||||||
|
}
|
||||||
|
return mimeType === validType
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
// export this package's api
|
||||||
|
import Upload from './Upload'
|
||||||
|
|
||||||
|
export default Upload
|
|
@ -0,0 +1,97 @@
|
||||||
|
function getError (option, xhr) {
|
||||||
|
const msg = `cannot post ${option.action} ${xhr.status}'`
|
||||||
|
const err = new Error(msg)
|
||||||
|
err.status = xhr.status
|
||||||
|
err.method = 'post'
|
||||||
|
err.url = option.action
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBody (xhr) {
|
||||||
|
const text = xhr.responseText || xhr.response
|
||||||
|
if (!text) {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(text)
|
||||||
|
} catch (e) {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// option {
|
||||||
|
// onProgress: (event: { percent: number }): void,
|
||||||
|
// onError: (event: Error, body?: Object): void,
|
||||||
|
// onSuccess: (body: Object): void,
|
||||||
|
// data: Object,
|
||||||
|
// filename: String,
|
||||||
|
// file: File,
|
||||||
|
// withCredentials: Boolean,
|
||||||
|
// action: String,
|
||||||
|
// headers: Object,
|
||||||
|
// }
|
||||||
|
export default function upload (option) {
|
||||||
|
const xhr = new window.XMLHttpRequest()
|
||||||
|
|
||||||
|
if (option.onProgress && 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 window.FormData()
|
||||||
|
|
||||||
|
if (option.data) {
|
||||||
|
Object.keys(option.data).map(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 () {
|
||||||
|
// allow success when 2xx status
|
||||||
|
// see https://github.com/react-component/upload/issues/34
|
||||||
|
if (xhr.status < 200 || xhr.status >= 300) {
|
||||||
|
return option.onError(getError(option, xhr), getBody(xhr))
|
||||||
|
}
|
||||||
|
|
||||||
|
option.onSuccess(getBody(xhr), xhr)
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr.open('post', option.action, true)
|
||||||
|
|
||||||
|
// Has to be after `.open()`. See https://github.com/enyo/dropzone/issues/179
|
||||||
|
if (option.withCredentials && 'withCredentials' in xhr) {
|
||||||
|
xhr.withCredentials = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = option.headers || {}
|
||||||
|
|
||||||
|
// when set headers['X-Requested-With'] = null , can close default XHR header
|
||||||
|
// see https://github.com/react-component/upload/issues/33
|
||||||
|
if (headers['X-Requested-With'] !== null) {
|
||||||
|
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const h in headers) {
|
||||||
|
if (headers.hasOwnProperty(h) && headers[h] !== null) {
|
||||||
|
xhr.setRequestHeader(h, headers[h])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.send(formData)
|
||||||
|
|
||||||
|
return {
|
||||||
|
abort () {
|
||||||
|
xhr.abort()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
const now = +(new Date())
|
||||||
|
let index = 0
|
||||||
|
|
||||||
|
export default function uid () {
|
||||||
|
return `rc-upload-${now}-${++index}`
|
||||||
|
}
|
Loading…
Reference in New Issue