运行效果图
一、组件介绍
基本特点
基于HTML5的FileReader和FormData
可以完成多文件选择,并预览
完成文件的异步上传
原生XHR对象,适配多浏览器
代码
class JohnUploader{
url;
fileField;
vollay;
/**
*
* @param url 文件上传的地址
* @param fileField 一个"文件域"对象
* @param vollay 一个HTMLElement对象,做为img的容器
*/
constructor(url,fileField,vollay){
this.url=url
this.fileField=fileField
this.vollay=vollay
}
/**
* @param nf 一个新的"文件域对象"
* 由于"文件域"是不能够改变内容,所以需要改变这个属性
*/
setFileField(nf){
this.fileField=nf
}
/**
* 本函数的触发时机--文件域的改变事件
* 作用:在画廊中显示选中的图片
*/
selectionShow() {
this.vollay.innerHTML="";
let files = this.fileField.files;
for (let i = 0; i < files.length; i++) {
let file = files[i]
if(!file.isRemoved) {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = event=> {
let img = document.createElement('img')
img.src = reader.result
//点击图片删除(以后改成点击图片上的"删除logo")
img.onclick = event=> {
//为文件加入删除标记
file.isRemoved=true
//重新刷新画廊,从而不显示有删除标记的文件
this.selectionShow()
}
this.vollay.appendChild(img)
}
}
}
};
/**
* //根据给定的表单域,完成文件上传
* @param callback 文件上传完毕的回调函数,callback中的参数为:xhr.reponseText
*/
uploadFile(callback) {
let formData=new FormData();
let files = this.fileField.files;
if(files.length==0||files===null){
alert("没有选择上传文件!")
return;
}
for (let i = 0; i < files.length; i++) {
let file=files[i]
//如果文件没有加删除标记
if(!file.isRemoved)
formData.append('avatar',files[i],files[i].name);
}
let xhr=new XMLHttpRequest();
xhr.open("POST",this.url)
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
callback(xhr.responseText)
}
}
xhr.send(formData)
}
}
二、组件使用演示
HTML部分
Titleimg {
height: 100px;
margin: 5px;border: darkgreen 3px solid;padding: 2px;
}
这个文件域是被隐藏掉了
选择要上传的图片
上传画廊中的图片
已经上传的文件
//底部的测试代码
js测试代码
window.οnlοad=function(){
//抓取后台的图片列表
function fetchAllPhotos(url,callback){
let xhr=new XMLHttpRequest();
xhr.open("GET",url)
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
let photos=JSON.parse(xhr.responseText)
callback(photos)
}
}
xhr.send(null)
}
/**
* 将抓取到的图片列表,在targetLocation中显示出来
* @param photos
* @param targetLocation
*/
function fetchAllPhotosCallback(photos,targetLocation){
targetLocation.innerHTML=''
photos.forEach(photo=>{
let img=document.createElement('img')
img.src='images/'+photo
targetLocation.appendChild(img)
})
}
let vollay = document.querySelector("#vollay")
let avatar = document.querySelector('[name="avatar"]')
let photoWall=document.querySelector('#photo-wall')
//这是主角JohnUploader
let uploader=new JohnUploader('upload',avatar,vollay)
//用来处理文件域清空的特殊情况,将来使用该克隆体,再进行克隆,替换掉avatar
let avtarClone=avatar.cloneNode(true)
//用于将"画廊复位"和将"文件域"进行复位
function reset(){
vollay.innerHTML = ''
let avatarClone2=avtarClone.cloneNode(true)
uploader.setFileField(avatarClone2)
avatar.after(avatarClone2)
avatar.remove()
avatar=avatarClone2
avatar.onchange = function(){
uploader.selectionShow()
}
}
//文件域的变化事件
avatar.onchange = function(){
uploader.selectionShow()
}
//抓取并显示后台的所有图片到照片墙
fetchAllPhotos('files',photos=>fetchAllPhotosCallback(photos,photoWall))
//使用button来完成"文件域"的选择文件功能
document.querySelector('#select-file').οnclick=()=>avatar.click()
//文件上传按钮的事件处理
document.querySelector('#upload-file').οnclick=()=> {
let innerAvatar=avatar
uploader.uploadFile(txt => {
//抓取并显示后台的所有图片到照片墙
fetchAllPhotos('files', photos => {
fetchAllPhotosCallback(photos, photoWall)
reset()
})
})
}
}
三、服务器部分Express+multer
项目依赖:
express
multer
项目结构
项目结构
代码
//app.js
const fs=require('fs')
const express=require('express')
const http=require('http')
//文件上传中间件(指定上传的临时文件夹是/uploads)
const multer=require('multer')
var storage = multer.memoryStorage()
//磁盘临时文件的方案
// let upload = multer({ dest: 'uploads/' })
//内存缓存方案
let upload = multer({ storage: storage })
let app=express();
const FILE_PATH="public/images/"
//HttpServer服务的中间件(public目录下的index.html为首页)
app.use(express.static('public'))
//文件上传的处理(avatar是上传时的filedName)
app.post('/upload', upload.array('avatar',10), function (req, res, next) {
//req.body是普通表单域
//req.files或req.file,是文件域
let msg={
body:req.body,
files:req.files
}
//磁盘临时文件方案,将临时文件上传到/public/images中
// let output=fs.createWriteStream(FILE_PATH+req.file.originalname)
// let input=fs.createReadStream(req.file.path)
// input.pipe(output)
//内存缓存方案
req.files.forEach((file,index)=>{
fs.writeFile(FILE_PATH+file.originalname,file.buffer,function () {
console.log(file.originalname+"....完成"+index)
//最后一个文件处理完毕,直接显示数据
if (index==req.files.length-1){
res.json(msg)
}
})
})
})
//接收前端的请求,返回上传图片的列表
app.get("/files",function (req,res) {
fs.readdir('public/images',function (err,dir) {
res.json(dir)
})
})
//启动Express服务器
let server=http.createServer(app);
server.listen(8000,function () {
console.log("start server at port 8000")
})