上传文件
上传文件
multiple:接收多个文件上传
input自带的上传文件样式不好看,并且各个浏览器的默认样式不一致,我们需要修改
在外面添加一个盒子,然后将内部的 input 隐藏,修改外部盒子的样式就可以了,但是input隐藏后触发不了事件怎么办呢?这里我们可以使用模拟事件
img就是内部input元素,这样就可以触发选择文件的事件了。
inputBox.addEventListener('click', function () {
// 这里使用模拟事件
let evt = document.createEvent('MouseEvents')
// 初始化事件 三个参数分别为事件类型,是否冒泡,是否阻止默认事件
evt.initEvent('click', false, false)
img.dispatchEvent(evt) //触发事件
}, false)
接下来的功能是选择的文件预览
监听input选择的change事件,因为可能上传多个文件,这里循环处理,使用formdata上传文件,创建formdata实例,后面做判断,我这里只写了图片文件的预览,但是上传的话如果没有在input的属性中规定上传的文件类型,是什么文件都可以的。
在else中执行渲染操作,具体就是将图片转化为base64格式然后创建元素插入并渲染。
render.result就是拿到的base64字符串,这样的话文件的预览就实现了。
img.addEventListener('change', function () {
//this.files就是拿到的文件
console.log(this.files)
formdata = new FormData()
for (var i = 0; i < this.files.length; i++) {
if (this.files[i].type.includes('mp4')) {
formdata.append(`mp4${i}`, this.files[i]); //插入formdata
$span = $(`不支持该类型的预览`)
$(".imgbox").append($span);
} else {
let render = new FileReader()// 创建读取文件类
render.readAsDataURL(this.files[i]);// 读取对应的文件
formdata.append(`img${i}`, this.files[i]); //插入formdata
render.onload = () => {
// 图片预览
$img = $(``)
$(".imgbox").append($img);
}
}
}
})
上传
这里有两个实现,一个jquery还有一个axios,推荐使用axios,因为他将上传文件的操作独立出来了,可以更方便的实现进度条效果。
$("#btn").click(function (e) {
formdata.append('name', '1111')
if (!$('#img')[0].value) {
alert('上传的文件不能为空')
return false
}
// axios实现
axios({
method: 'post',
url: 'http://localhost:3000/users', //这里写node的接口
data: formdata,
headers: {
//这里必须要写请求头,不然node识别不了
'Content-Type': 'multipart/form-data'
},
onUploadProgress: function (progressEvent) {
console.log(progressEvent)
// 进度条
$(".speed").show()
let percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
$(".content").css("width", percentCompleted + "%")
if (percentCompleted == 100) {
setTimeout(() => {
$(".speed").hide()
}, 500)
}
}
}).then(res => {
//成功后清空最上面声明的formdata
formdata = new FormData()
}).catch(err => {
console.log(err)
})
// jquery实现
// $.ajax({
// url: "http://localhost:3000/users/?age=1",
// type: "POST",
// data: formdata,
// processData: false, // 表示不需要对数据进行处理
// cache: false,// 不需要对数据进行缓存
// contentType: false,// 不需要设置请求头
// xhr: function () {
// var xhr = new XMLHttpRequest();
// //使用XMLHttpRequest.upload监听上传过程,注册progress事件,打印回调函数中的event事件
// xhr.upload.addEventListener('progress', function (e) {
// //loaded代表上传了多少total代表总数为多少
// var progressRate = (e.loaded / e.total) * 100 + '%';
// //进度条
// $('.speed').css('display', 'block');
// $('.content').css('width', progressRate);
// if (progressRate == '100%') {
// setTimeout(() => {
// $('.speed').css('display', 'none');
// }, 500)
// }
// })
// return xhr
// },
// success: (res) => {
// // 清空上一次的文件(也就是新创建一个)
// formdata = new FormData()
// $('#img')[0].value = ''
// $(".imgbox").empty()
// },
// error: (err) => {
// console.log(err)
// }
// })
})
nodejs部分,这里主要是基于express,首先起服务就不说了
解决跨域,在App.js中添加,注意添加的位置
app.all('*', function (req, res, next) { //处理跨域
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By", ' 3.2.1')
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
node路由
这里我们需要安装一个模块formidable,使用yarn add formidable -s安装,npm安装也可以,推荐使用yarn,他会锁定当前项目依赖的版本,这样别人在下载依赖的时候就不会出现依赖版本问题。
下面可以实现基本的功能,也可以自己做判断分辨文件的类型啊,存储在不同的文件夹等等
// 解析带文件上传的表单需要
const formidable = require("formidable")
router.post('/', function (req, res, next) {
console.log(req.body)
var form = new formidable.IncomingForm()
// console.log(path.resolve(__dirname, '../'))
//path.resolve(__dirname, '../')代表当前目录的上一级__dirname表示当前的根目录
form.uploadDir = `${path.resolve(__dirname, '../')}/public/images` // 上传目录
form.keepExtensions = true //上传文件保持原来的扩展名
form.parse(req, function (err, fields, files) {
console.log(files) //上传的文件参数
console.log(fields) //上传的除了文件以外的参数
if (err) {
res.send(err)
}
let response = {
msg: `成功上传至${form.uploadDir}`
}
res.send(response)
})
});
从服务端下载文件
服务端部分,这里我们读取文件后直接返回文件的blob编码,我们在前端响应的时候将相应类型设置为buffer就可以接收到了,我这里还判断了前端需要下载什么类型的文件,目前是有视频和图片。
router.get('/download', async function (req, res, next) {
let { type } = req.query
let video = []
let images = []
let file = ''
const filesName = fs.readdirSync(path.resolve(__dirname, '../public/images'))
function filelist() {//读取文件列表
filesName.forEach(item => {
if (item.includes('mp4')) {
video.push(item)
} else {
images.push(item)
}
})
}
filelist()
function read(url) { //读取文件
filelist()
return new Promise((resolve, rejects) => {
//fs读取文件如果不添加第二个参数data是buffer类型的数据,如果添加的话就是字符串
fs.readFile(`${path.resolve(__dirname, '../')}/public/images/${url}`, function (err, data) {
resolve(data)
rejects(err)
})
})
}
if (type === 'mp4') {
if (video.length === 0) {
res.send({
msg: '视频不存在',
})
} else {
file = video[0]
let data = await read(file)
//如果要前端下载的话这个响应头必须设置
res.setHeader('Content-Type', 'application/octet-stream')
res.send(data)
}
} else {
if (images.length === 0) {
res.send({
msg: '图片不存在',
})
} else {
file = images[0]
let data = await read(file)
//如果要前端下载的话这个响应头必须设置
res.setHeader('Content-Type', 'application/octet-stream')
res.send(data)
}
}
});
前端
function download(type) {
axios.get(`http://localhost:3000/users/download?type=${type}`, {
responseType: 'blob',
// responseType: 'arraybuffer' //这两种格式都可以,都是二进制流,但是如果使用这个,就不能判断type值了
}).then(res => {
console.log(res)
if (res.data.type.includes('application/json')) {
// blob转json
var reader = new FileReader();
reader.readAsText(res.data,'utf-8');
reader.onload = function (event) {
content = JSON.parse(reader.result);
alert(content.msg)
};
} else {
let url = window.URL.createObjectURL(new Blob([res.data]))
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', '资源.' + type)
document.body.appendChild(link)
link.click()
document.body.removeChild(link) //下载完成移除元素
window.URL.revokeObjectURL(url) //释放掉blob对象
}
})
}