likes
comments
collection
share

用原生js加node数据接口实现头像上传功能

作者站长头像
站长
· 阅读数 10

前言

本文记录一下一个头像上传功能,前端与后端分别都需要做些什么。

前端代码:

  1. html部分
<!-- 头像上传区域 -->
<div class="upload-content">
    <!-- 利用类型为file的input标签实现上传 -->
    <input type="file" name="file" id="upload">
    <!-- img标签用于头像显示 -->
    <img src="" alt="预览图片" id="img" style="display: none;">
    <!-- 没有头像图片时显示该背景 -->
    <div class="bg-color" id="bg-color"></div>
</div>

注:上面的显示背景可改成img标签的默认图片,但我嫌懒得找,就直接给了个背景。

  1. css部分 需要讲一下此处的实现思路:需要将三个元素重叠在一起,上传的input元素在最高的层级,给上传的input设置透明度,这样按钮就看不见了(因为默认样式很丑),这样就可以看到没有头像时的背景,但是那个背景点击了之后就可以上传,因为有一层看不见的input在最外层。图片上传成功后,隐藏那个背景,显示图片即可。
.upload-content {
    position: relative;
}
#upload{
    position: absolute;
    top: 0;
    left: 0;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    opacity: 0;
    z-index: 10;
    outline: none;
    cursor: pointer;
}
.bg-color, #img{
    position: absolute;
    top: 0;
    left: 0;
    background-color: #ccc;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    cursor: pointer;
}

效果展示:

用原生js加node数据接口实现头像上传功能

  1. js代码:
const upload = document.getElementById("upload"); // 获取上传文件input元素
const img = document.getElementById("img"); // 获取显示图片的img元素
const bgColor = document.getElementById("bg-color"); // 没有头像时的背景
upload.onchange=function() { // input元素改变时触发
    if (upload.files && upload.files.length > 0) { // 如果选中了文件,并点击了确定,就有upload.files参数
        const file = upload.files[0]; // 获取文件数据
        const isImg = /.jpg$|.png$|.gif/.test(file.name); // 对文件格式进行校验,必须为图片格式
        if (isImg) { // 如果文件格式正确则执行上传
            const reader = new FileReader()
            reader.readAsDataURL(file);
            reader.onload = (e) => {
                let localSrc = e.target.result // bs64用于显示的
                event.target.value= '' // 解决上传第二次不能选择同一文件
                let rawFile = new FormData() // 声明一个FormData
                rawFile.append('file', file) // 将file文件数据转化成FormData的格式,如果不转成该格式则无法上传
                // img.src = localSrc;
                ajax(rawFile, 'post', '/api/file/upload'); // 调用上传接口
            }
        } else {
             console.warn("文件格式不正确!");
        }
    } else {
        console.warn("文件未上传!");
    }
}

const ajax = (params, mode, url) => { 
    const xhr = new XMLHttpRequest(); // 声明ajax对象
    xhr.open(mode, 'http://localhost:5000' + url, true); // 传入请求方式和请求接口,最后一个参数为异步
    xhr.setRequestHeader('systemType', 'supplyChain'); // 此为上传文件必须设置的请求头
    xhr.send(params); // 将转换后的FormData数据传入接口
    xhr.onload = () => {
        const url = 'http://localhost:5000/api/file/load/' + JSON.parse(xhr.response).url; // 可以预览,也可以下载的头像地址
        console.log(url); // 在控制台点击该地址即可下载
        img.style.display = "block"; // 上传后img标签显示
        bgColor.style.display = "none"; // 上传后背景隐藏
        img.src = url; // 显示上传后的图片
    }
}

效果展示:

用原生js加node数据接口实现头像上传功能

后端代码:

上传接口

  1. 先安装express和multer
npm install express --save 
npm install multer --save
  1. 引入express和multer
const express = require('express');
const router = express.Router();
const multer = require('multer');
  1. 设置保存到那个文件夹,以及文件名设置
const storage = multer.diskStorage({
    destination(req, file, cb) {
        cb(null, 'file') // 设置文件保存的文件夹,存储的文件夹必须自己先创建,如果没有的话会报错,只需要文件名,不需要改路径
    },
    filename(req, file, cb) {
        cb(null, randomNumber(1,1000000) + '-' + file.originalname) // 设置保存的文件名
    }
});

// 随机数
const randomNumber = (min, max) => {
    return Math.floor(Math.random() * (max - min) + min);
}

注:我给文件名拼接了一个随机数在前面,原因是如果两个文件内容不同但文件名相同后上传的文件会覆盖之前上传的文件,加一个随机数之后每一次上传都能上传成功,不会被覆盖。

  1. 上传成功,返回文件名
// 文件上传
router.post('/upload', multer({storage: storage}).single('file'), (req, res, next) => {
    let data = {
        code: 200,
        url: req.file.filename
    }
    res.send(data);
})

下载接口

  1. 引入node自带的fs和path模块儿
const fs = require('fs');
const path = require('path');
  1. 文件下载具体实现
// 文件预览
router.get('/load/:fileName', (req, res, next) => { 
    let fileName = req.params.fileName; // 访问的文件名
    let filePath = path.join(__dirname, "../../file/" + fileName); // 当前项目中文件的存储地址,filePath打印出来应该能访问到上传上去的文件地址路径才是正确的
    let stats = fs.statSync(filePath);
    if(stats.isFile()){
        res.set({
          'Content-Type': 'application/octet-stream;', // 告诉浏览器,返回的是一个二进制文件
          'Content-Disposition': 'attachment;filename=' + encodeURIComponent(fileName), // 返回的文件名,如果不加encodeURIComponent,中文文件名的文件是访问不了也下载不了的
          'Content-Length': stats.size // 告诉浏览器,返回的文件大小
        });
        fs.createReadStream(filePath).pipe(res); // 返回一个二进制流
    } else {
       res.send(400);
    }
})

前端实现下载功能的补充:

  1. 可以用a标签打开文件地址进行文件下载
  2. 可以使用window.open实现文件下载
  3. 可以用ajax实现文件下载:
/**
 * @description: 用请求下载图片 
 * @param { string } href 需要下载的文件地址
 * @param { string } name 文件名
 * @return {*}
 */
const xhrDownFile = (href, name) => {
    let xhr = new XMLHttpRequest();
    xhr.open('GET', href, true);
    xhr.responseType = 'blob';
    xhr.onload = function() {
        if (this.status === 200) {
            let blob = this.response;
            let a = document.createElement('a');
            let url = window.URL.createObjectURL(blob);
            a.href = url;
            a.download = name
            a.click();
            a.remove();
        }
    }
    xhr.send();
}

注:上述方式只需要传入文件名和访问地址即可,但是不能携带token,如果要携带token自己改写!上述的文件预览接口直接window.open前端拼接的文件地址即可。