1、首先图片上传是需要有服务器支持的,这里我们用node在本地起一个服务,模拟服务器接收上传图片并保存,再通过img标签的url链接拿到服务器的图片地址。
2、多文件上传在前端只需要在类型为file的input的属性中增加multiple属性即可多选,文件格式同理在input上增加accept定义文件格式。服务器上也需要做相应的配置才能支持多文件上传。
3、整个图片上传的流程是:用户点击上传按钮,组件触发类型为file的input,用户选择完文件确定后,组件循环拿到文件数组中的文件并上传给服务器,此时文件的状态为上传中,上传成功后服务器保存文件后接口便返回给前端url路径,此时文件状态为成功,若上传过程中失败则状态为失败。上传成功后,img标签展示接口返回的url链接完成图片预览。整个图片上传流程结束。
那我们依次来看看每一个步骤的代码吧。
index.js
const express = require('express')
const multer = require('multer')
const cors = require('cors')
const upload = multer({dest: 'images/'})
const app = express()
app.get('/', (req, res) => {
res.send('hello word')
})
app.options('/upload', cors())
app.post('/upload', cors(), upload.single('file'), (req, res)=> {
res.send(JSON.stringify({id: req.file.filename}))
})
app.get('/preview/:key', cors(), (req, res)=> {
res.sendFile(`images/${req.params.key}`, {
root: __dirname,
headers: {
'Content-Type': 'image/jpeg',
}
}, (error)=>{
console.log(error)
})
})
var port = process.env.PORT || 3000
console.log(port)
app.listen(port)
启动命令 node index
上传
export default {
data() {
return {
fileList: [] //图片上传成功的url数组
}
},
methods: {
parseResponse(response) {
let object = JSON.parse(response)
return `http://127.0.0.1:3000/preview/${object.id}`
},
xxx(fileList) {
// console.log('监听到了 update:fileList 事件')
// console.log(fileList, 'fileList')
}
},
}
-
{{file.name}}
export default {
props: {
name: {
type: String,
required: true
},
action: {
type: String,
required: true
},
accept: {
type: String,
default: 'image/*'
},
method: {
type: String,
default: 'POST'
},
parseResponse: {
type: Function,
required: true
},
fileList: {
type: Array,
default: ()=> []
},
sizeLimit: {type: Number}
},
methods: {
onClickUpload() {
let input = this.createInput()
input.addEventListener('change', ()=> {
this.uploadFiles(input.files) //单文件
input.remove()
})
input.click()
},
uploadFiles(rawFiles) {
let newNames = []
for (let i=0; i {
let url = this.parseResponse(response)
this.afterUploadFiles(newName, url)
}, (xhr)=> {
this.uploadError(xhr, newName)
})
}
},
beforeUploadFiles(rawFiles, newNames) {
rawFiles = Array.from(rawFiles)
for (let i=0; i< rawFiles.length; i++) {
let {size, type} = rawFiles[i]
if (size > this.sizeLimit) {
this.$emit('error', `文件不能大于${this.sizeLimit * 1024}M`)
return false
}
}
let x = rawFiles.map((rwaFile, i)=> {
let {type, size} = rwaFile
return {name: newNames[i], type, size, status: 'uploading'}
})
this.$emit('update:fileList', [...this.fileList, ...x])
return true
},
afterUploadFiles(newName, url) {
let file = this.fileList.filter(f=> f.name === newName)[0]
let index = this.fileList.indexOf(file)
let fileCopy = JSON.parse(JSON.stringify(file))
fileCopy.url = url
fileCopy.status = 'success'
let fileListCopy = [...this.fileList]
fileListCopy.splice(index, 1, fileCopy)
this.$emit('update:fileList', fileListCopy)
this.$emit('uploaded') //全部上传完成的回调
},
generateName(name) {
while (this.fileList.filter(f => f.name === name).length > 0) {
let duIndexOf = name.lastIndexOf('.')
let nameBefore = name.substring(0, duIndexOf)
let nameAfter = name.substring(duIndexOf)
name = nameBefore + '(1)' + nameAfter
}
return name
},
uploadError(xhr, newName) {
let file = this.fileList.filter(f => f.name === newName) [0]
let index = this.fileList.indexOf(file)
let fileCopy = JSON.parse(JSON.stringify(file))
fileCopy.status = 'fail'
let fileListCopy = [...this.fileList]
fileListCopy.splice(index, 1, fileCopy)
this.$emit('update:fileList', fileListCopy)
let error = ''
if (xhr.status === 0) { // xhr === XMLHttpRequest
error = '网络无法连接'
}
this.$emit('error', error)
},
onRemoveFile(file) {
let answer = window.confirm('你确定要删除它吗?')
if (answer) {
let copy = [...this.fileList]
let index = copy.indexOf(file)
copy.splice(index, 1)
this.$emit('update:fileList', copy)
}
},
createInput() {
this.$refs.box.innerHTML = ''
let input = document.createElement('input')
input.accept = this.accept
input.type= 'file'
input.multiple = true
this.$refs.box.appendChild(input)
return input
},
doUploadFile(formData, success, fail) {
$http[this.method.toLowerCase()](this.action, {success, fail, data: formData}) //本地模拟接口请求
}
}
}
完整代码可参考我的GitHub:https://github.com/A-Tione/tione/blob/master/src/upload/upload.vue