美文网首页
一个基于HTML5及原生JS的文件上传组件--JohnUploa

一个基于HTML5及原生JS的文件上传组件--JohnUploa

作者: JohnYuCN | 来源:发表于2019-12-07 17:43 被阅读0次
运行效果图

一、组件介绍

基本特点

  1. 基于HTML5的FileReader和FormData
  2. 可以完成多文件选择,并预览
  3. 完成文件的异步上传
  4. 原生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)
    }
}

二、组件使用演示

  1. HTML部分
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        img {
            height: 100px;
            margin: 5px;border: darkgreen 3px solid;padding: 2px;
        }
    </style>
    <script src="johnuploader.js"></script>
</head>
<body>

<h2>这个文件域是被隐藏掉了</h2>
<button id="select-file">选择要上传的图片</button>
<button id="upload-file">上传画廊中的图片</button>
<div id="upload-groups">
    <input type="file" name="avatar" multiple style="display: none">
</div>
<div id="vollay">
<!--待上传文件的预览区域-->
</div>
<hr>

<h2>已经上传的文件</h2>
<div id="photo-wall">
</div>
<script>
//底部的测试代码
</script>
</body>
</html>
  1. js测试代码
    window.onload=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').onclick=()=>avatar.click()

        //文件上传按钮的事件处理
        document.querySelector('#upload-file').onclick=()=> {
            let innerAvatar=avatar
            uploader.uploadFile(txt => {
                //抓取并显示后台的所有图片到照片墙
                fetchAllPhotos('files', photos => {
                    fetchAllPhotosCallback(photos, photoWall)
                    reset()
                })
            })
        }
    }

三、服务器部分Express+multer

  1. 项目依赖:
    express
    multer

  2. 项目结构

项目结构
  1. 代码

//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")
})

相关文章

网友评论

      本文标题:一个基于HTML5及原生JS的文件上传组件--JohnUploa

      本文链接:https://www.haomeiwen.com/subject/dqvugctx.html