入门文件上传

200 阅读2分钟

由于最近项目有大文件上传,所以最近复习了下文件上传的相关知识,特别整理了一下笔记

js 实现图片预览的两种方式

  • 要操作文件(图片)首先需要html支持,html中支持操作选择文件的标签如下
<input type="file" />
<!-- 下面图片预览代码的html结构 -->
  <input type="file" onchange="handleFileChange1(this)" />
  
  <input type="file" onchange="handleFileChange2(this)" />
  
  • 第一种方式:(使用bloburl)js中的核心API是 URL.createObjectURL
   
    // 2通过 URL.createObjectURL,它会把文件对象变成 blob 对象,然后直接被 img 标签读取

    function handleFileChange2(instance) {
      const file = instance.files[0];
      imgUrl = globalThis.URL.createObjectURL(file);
      const img = new Image();
      img.alt = img.title = file.name;
      img.src = imgUrl;
      document.body.appendChild(img);
    }

图片预览方式一.png

  • 第二种方式:(使用dataurl)比较消耗性能 js中的核心API是 FileReader readAsDataURL
  // 1通过 readAsDataURL 把图片转换为base64
    function handleFileChange1(instance) {
      const file = instance.files[0];
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = (e) => {
        const base64URL = e.target.result;
        const img = new Image();
        img.alt = img.title = file.name;
        img.src = base64URL;
        document.body.appendChild(img);
      };
    }

图片预览方式二.png

js 实现单张图片上传的两种方式

<!-- 下面图片预览代码的html结构 -->
    <input type="file" id="file1">

    <input type="file" id="file2">
  
  • 方案1:基于FORM-DATA
      // 基于formData
      document.getElementById('file2').onchange = async function() {
        var files = this.files
        if (files.length <= 0) return
        var formData = new FormData()
        formData.append('file', files[0])
        formData.append('filename', files[0].name)
        var res = await axios.post('http://127.0.0.1:8888/single1', formData)
        if (res.data.code == 0) {
          var img = new Image()
          img.src = res.data.path
          document.body.appendChild(img)
        }
      }


  • 方案2:把选择的文件转化为BASE64,把BASE64传递给服务器
      // 基于base64

        function getBase64(file) {
          return new Promise((resolve, reject) => {
            var ready = new FileReader()
            ready.readAsDataURL(file)
            ready.onload = function(e) {
              resolve(e.target.result)
            }
          })
        }
      document.getElementById('file1').onchange = async function() {
        var files = this.files
        if (files.length <= 0) return
        // console.log(files)
        var base64 = await getBase64(files[0])
        var filename = files[0].name
        // console.log(filename, base64)
        var res = await axios.post('http://127.0.0.1:8888/single2', Qs.stringify({ chunk: encodeURIComponent(base64), filename }), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } })
        if (res.data.code == 0) {
          var img = new Image()
          img.src = res.data.path
          document.body.appendChild(img)
        }
      }

js 实现多张图片上传

    <section class="uploadBox clearfix">
        <div class="card button">
            <input type="file" id="uploadInp" accept="image/*" multiple>
        </div>

        <!-- <div class="card">
            <img src="images/1.png" alt="">
            <div class="progress">
                <div class="line"></div>
            </div>
            <div class="mark"></div>
        </div> -->
    </section>
  (function () {
            //请求封装
            function postRequest(url, data, config) {
                config = config || {};
                return axios.post(`http://127.0.0.1:8888${url}`, data, config).then(response => {
                    return response.data;
                });
            }

            //文件读取
            function fileReader(file) {
                return new Promise(resolve => {
                    let reader = new FileReader;
                    reader.readAsDataURL(file);
                    reader.onload = ev => {
                        resolve(ev.target.result);
                    };
                });
            }

            //延迟函数
            function delay(interval) {
                interval = interval || 500;
                return new Promise(resolve => {
                    setTimeout(() => {
                        resolve();
                    }, interval);
                });
            }

            let uploadBox = document.querySelector('.uploadBox'),
                button = uploadBox.querySelector('.button'),
                uploadInp = uploadBox.querySelector('#uploadInp');
            button.onclick = function () {
                uploadInp.click();
            };
            uploadInp.onchange = async function () {
                let self = this,
                    files = Array.from(self.files);
                if (files.length === 0) return;

                // 构建上传列表
                let uploadList = [];
                files.forEach((file, index) => {
                    uploadList[index] = {
                        file: file,
                        base64: null,
                        card: null
                    };
                });

                // 搞定BASE64 && 和动态创建CARD
                let base64List = await Promise.all(files.map(file => fileReader(file))),
                    frag = document.createDocumentFragment();
                base64List.forEach((base64, index) => {
                    let card = document.createElement('div');
                    card.className = 'card';
                    card.innerHTML = `
                        <img src="${base64}" alt="">
                        <div class="progress">
                            <div class="line"></div>
                        </div>
                        <div class="mark"></div>
                    `;
                    frag.appendChild(card);
                    //完善上传列表
                    uploadList[index].base64 = base64;
                    uploadList[index].card = card;
                });
                uploadBox.appendChild(frag);

                await delay();

                // 按照上传列表,批量上传图片 && 监听进度
                uploadList.forEach(async item => {
                    let {
                        file,
                        base64,
                        card
                    } = item;

                    let data = {
                            chunk: encodeURIComponent(base64),
                            filename: file.name
                        },
                        config = {
                            headers: {
                                "Content-Type": "application/x-www-form-urlencoded"
                            },
                            // 上传进度检测
                            onUploadProgress(ev) {
                                // ev.loaded && ev.total
                                let ratio = ev.loaded / ev.total * 100 + '%';
                                card.querySelector('.line').style.width = ratio;
                            }
                        };
                    let response = await postRequest('/single2', Qs.stringify(data), config);
                    if (response.code === 0) {
                        // 上传成功
                        await delay();
                        let progress = card.querySelector('.progress'),
                            mark = card.querySelector('.mark');
                        card.removeChild(progress);
                        card.removeChild(mark);
                    }
                });
            };
        })();

文件上传服务端参考代码如下

//后端代码
/*-CREATE SERVER-*/
const express = require('express'),
    app = express(),
    bodyParser = require('body-parser'),
    fs = require('fs'),
    SparkMD5 = require('spark-md5'),
    PORT = 8888;
app.listen(PORT, () => {
    console.log(`THE WEB SERVICE IS CREATED SUCCESSFULLY AND IS LISTENING TO THE PORT:${PORT}`);
});
app.use((req, res, next) => {
    res.header("Access-Control-Allow-Origin", "*");
    req.method === 'OPTIONS' ? res.send('CURRENT SERVICES SUPPORT CROSS DOMAIN REQUESTS!') : next();
});
app.use(bodyParser.urlencoded({
    extended: false,
    limit: '1024mb'
}));

/*-API-*/
const multiparty = require("multiparty"),
    uploadDir = `${__dirname}/upload`;

function handleMultiparty(req, res, temp) {
    return new Promise((resolve, reject) => {
        // multiparty的配置
        let options = {
            maxFieldsSize: 200 * 1024 * 1024
        };
        !temp ? options.uploadDir = uploadDir : null;
        let form = new multiparty.Form(options);
        // multiparty解析
        form.parse(req, function (err, fields, files) {
            if (err) {
                res.send({
                    code: 1,
                    reason: err
                });
                reject(err);
                return;
            }
            resolve({
                fields,
                files
            });
        });
    });
}

// 基于FORM-DATA上传数据
app.post('/single1', async (req, res) => {
    let {
        files
    } = await handleMultiparty(req, res);
    let file = files.file[0];
    res.send({
        code: 0,
        originalFilename: file.originalFilename,
        path: file.path.replace(__dirname, `http://127.0.0.1:${PORT}`)
    });
});

// 上传BASE64
app.post('/single2', (req, res) => {
    let {
        chunk,
        filename
    } = req.body;

    // chunk的处理:转换为buffer
    chunk = decodeURIComponent(chunk);
    chunk = chunk.replace(/^data:image\/\w+;base64,/, "");
    chunk = Buffer.from(chunk, 'base64');

    // 存储文件到服务器
    let spark = new SparkMD5.ArrayBuffer(),
        suffix = /\.([0-9a-zA-Z]+)$/.exec(filename)[1],
        path;
    spark.append(chunk);
    path = `${uploadDir}/${spark.end()}.${suffix}`;
    fs.writeFileSync(path, chunk);
    res.send({
        code: 0,
        originalFilename: filename,
        path: path.replace(__dirname, `http://127.0.0.1:${PORT}`)
    });
});

app.use(express.static('./'));
app.use((_, res) => {
    res.status(404);
    res.send('NOT FOUND!');
});