運行效果圖
一、組件介紹
基本特點
基于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")
})