分享

文件upload 文件上传深入

 羊玉wngbx 2021-04-16

我记录这篇文章是因为开发过程中,发现上传业务有时候感觉不同平台自己有时一脸懵逼不知道咋样去优化这块业务,不同的后台实现咋样做不同的处理,以下介绍后台实现主要node和java为主,比如:手机端app图片上传,H5页面图片上传,后台管理系统不分离时的图片上传,分离后使用前端框架图片上传这里主要介绍vue,混合框架uni-app图片上传,还有多图片上传优化等

H5 页面文件上传

敬请期待

pc端文件上传(分离)

敬请期待

pc端文件上传(不分离)

1. node+ejs

  • nodejs中在上传文件时,我们通常会使用到Multer中间件用于处理multipart/form-data 类型的表单数据。
cnpm install multer --save
  • 视图ejs使用form表单提交,enctype为:multipart/form-data表单类型

ejs

 <form action="http://localhost:8080/" method="post" enctype="multipart/form-data">
    <input type="file" name="files" value="指定文件">
    <br><br>
    <input type="submit" value="上传">
</form>

node

const express=require("express");
const multer=require('multer');
//初始化上传对象
var upload=multer({dest:'./upload/'});//目录要存在
var fs = require('fs');
​
​
var app=express();
​
app.use("/",upload.single("files"),function(req,res){   //files为input type="file"的name值
    var oldFile=req.file.destination+req.file.filename; //指定旧文件
    var newFile=req.file.destination + req.file.originalname;   //指定新文件
    fs.rename(oldFile,newFile,function(err){
        if(err){
            res.send('上传失败!');
        }else{
            res.send('上传成功!');
        }
    });
});
​
app.listen(8080);
  • 上传多个文件
    1. 首先要在ejs中form的input 加上 multiple
    2. 使用multer中间的方法 变为 array("files",5),通过遍历处理数据
  • 总结
    1. 在HTML找中input type="file"需要加上multiple来实现过滤,multiple不写参数则可以读取·所有文件。而在服务端上,我们需要将single()改为array(“name”,num);的形式来接收多个文件的上传请求。最后对他们全部进行重命名。在这之前我们首先看看multer支持哪些文件上传方式:
        .single(fieldname)  //接受一个以 fieldname 命名的文件。.fields(fields)
        .array(fieldname[, maxCount])   //接受一个以 fieldname 命名的文件数组。可以配置 maxCount 来限制上传的最大数量。
        .fields(fields) //接受指定 fields 的混合文件。fields是一个拥有name和maxCount的数组对象。
        .none()     //只接受文本域。如果任何文件上传到这个模式,将发生 "LIMIT_UNEXPECTED_FILE" 错误。
        .any()  //接受一切上传的文件。
    
    1. 通过limits来限制上传文件
        var upload=multer({dest:'./upload/',limits:{fileSize: 1024 * 1024 * 20,files: 5}});
    
    limits参数
        Key Description Default
        fieldNameSize   field 名字最大长度    100 bytes
        fieldSize   field 值的最大长度    1MB
        fields  非文件 field 的最大数量 无限
        fileSize    在 multipart 表单中,文件最大长度 (字节单位)   无限
        files   在 multipart 表单中,文件最大数量  无限
        parts   在 multipart 表单中,part 传输的最大数量(fields + files)    无限
        headerPairs 在 multipart 表单中,键值对最大组数 2000
        如果你上传的文件超出这些设定,MulterError模块将会启用,该模块在node_modules/multer/lib/multer-error.js上:
    

2. java + themlef

敬请期待

uni-app上传

  1. 通过官方文档我们掌握到图片上传核心的api,举个栗子:

    正常情况下:

uni.chooseImage({
    count: 1, //默认9
    sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有//不支持H5
    sourceType: ['album', 'camera'], //从相册选择 照相
    success: res => {
        console.log(res);//
        uni.showLoading({
            mask: true,
        })
        uni.uploadFile({
            url: 'http:/xxxx/api/common/image/upload', //仅为示例,非真实的接口地址
            filePath: res.tempFilePaths[0],
            name: 'file', //相当于formData提交key属性,后台可以根据key获取2进制数据
            success: (uploadFileRes) => {
                //根据后台返回的结果去获取对应的图片路径
                let imgUrl = JSON.parse(uploadFileRes.data).data,
            },
            complete: () => {
                uni.hideLoading()
                this.hideInputPopup()
            }
        });
    }
});
  1. 使用混合框架我们要注意选项的参数差异性

    [图片上传失败...(image-c47855-1604483474514)]

  2. 了解File类型到底是什么?

  • 计算机单位之间的转换:

    T(TB):我们所说的1个T
    G(GB):我们所说的1个G
    M(MB):万字节,也就是我们所说的兆
    K(KB):千字节
    B(Byte):字节 计算机显示的最小单位。
    bit:二进制位

    8bit == 1B(Byte)八个二进制位等于一个字节

    微软                                        计算机厂商
    

    1024B(Byte)= 1K(KB) 1000B(Byte)= 1K(KB)
    1024K(KB)= 1M(MB) 1000K(KB)= 1M(MB)
    1024M(MB)= 1G(GB) 1000M(MB)= 1G(GB)
    1024G(GB)= 1T(TB) 1000G(GB)= 1T(TB)

  • 打印通过uni.chooseImage获取的图片 内容是什么
    [图片上传失败...(image-3551bb-1604483474514)]

  1. H5中uni-app不支持H5压缩图片,那H5咋样进行图片压缩?
// 图片压缩
export function photoCompress(file,w,objDiv){
    var ready=new FileReader();
    /*开始读取指定的Blob对象或File对象中的内容. 当读取操作完成时,readyState属性的值会成为DONE,如果设置了onloadend事件处理程序,则调用之.同时,result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容.*/
    ready.readAsDataURL(file);
    ready.onload=function(){
        var re=this.result;
        canvasDataURL(re,w,objDiv)
    }
}
// 通过canvas压缩得到base64
export function canvasDataURL(path, obj, callback){
    var img = new Image();
    img.src = path;
    img.onload = function(){
        var that = this;
        // 默认按比例压缩
        var w = that.width,
            h = that.height,
            scale = w / h;
        w = obj.width || w;
        h = obj.height || (w / scale);
        var quality = 0.7;  // 默认图片质量为0.7
        //生成canvas
        var canvas = document.createElement('canvas');
        var ctx = canvas.getContext('2d');
        // 创建属性节点
        var anw = document.createAttribute("width");
        anw.nodeValue = w;
        var anh = document.createAttribute("height");
        anh.nodeValue = h;
        canvas.setAttributeNode(anw);
        canvas.setAttributeNode(anh);
        ctx.drawImage(that, 0, 0, w, h);
        // 图像质量
        if(obj.quality && obj.quality <= 1 && obj.quality > 0){
            quality = obj.quality;
        }
        // quality值越小,所绘制出的图像越模糊
        var base64 = canvas.toDataURL('image/jpeg', quality);
        // 回调函数返回base64的值
        callback(base64);
    }
}
  1. 使用uni-app实现图片上传并压缩,并且在提交一步之前不与后台交互,目的用户体验更好上传时不用等待。
    核心js方法配合使用 utils.js
// 上传图片(不交互的)
export function showImg() {
    return new Promise((resolve, reject) => {
        uni.chooseImage({
            count: 1, //默认9
            sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
            sourceType: ['album', 'camera'], //从相册选择
            success: res => {
                console.log("chooseImage",res)
                let _thumbnail = res.tempFilePaths[0];
                let _file = res.tempFiles[0];
                let _temp = {
                    blob: _thumbnail,
                    file: _file
                }
                resolve(_temp);
            }
        });
    })
}
//上传图片(交互的)
export function uploadImg(filePath) {
    return new Promise((resolve, reject) => {
        uni.showLoading({
            mask: true,
        })
        uni.uploadFile({
            url: uploadUrl, 
            name: "file",
            filePath: filePath,
            success: (uploadFileRes) => {
                let _thumbnail = JSON.parse(uploadFileRes.data).data;
                resolve(_thumbnail)
            },
            complete: () => {
                uni.hideLoading()
                // this.hideInputPopup()
            }
        });
    })  
}



// 图片压缩
export function photoCompress(file,w,objDiv){
    var ready=new FileReader();
    /*开始读取指定的Blob对象或File对象中的内容. 当读取操作完成时,readyState属性的值会成为DONE,如果设置了onloadend事件处理程序,则调用之.同时,result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容.*/
    ready.readAsDataURL(file);
    ready.onload=function(){
        var re=this.result;
        canvasDataURL(re,w,objDiv)
    }
}
// 通过canvas压缩得到base64
export function canvasDataURL(path, obj, callback){
    var img = new Image();
    img.src = path;
    img.onload = function(){
        var that = this;
        // 默认按比例压缩
        var w = that.width,
            h = that.height,
            scale = w / h;
        w = obj.width || w;
        h = obj.height || (w / scale);
        var quality = 0.7;  // 默认图片质量为0.7
        //生成canvas
        var canvas = document.createElement('canvas');
        var ctx = canvas.getContext('2d');
        // 创建属性节点
        var anw = document.createAttribute("width");
        anw.nodeValue = w;
        var anh = document.createAttribute("height");
        anh.nodeValue = h;
        canvas.setAttributeNode(anw);
        canvas.setAttributeNode(anh);
        ctx.drawImage(that, 0, 0, w, h);
        // 图像质量
        if(obj.quality && obj.quality <= 1 && obj.quality > 0){
            quality = obj.quality;
        }
        // quality值越小,所绘制出的图像越模糊
        var base64 = canvas.toDataURL('image/jpeg', quality);
        // 回调函数返回base64的值
        callback(base64);
    }
}

template

<view class="upload-box" @tap="uploadImg">
    <view class="img-box">
        <image class="img" v-if="thumbnail" :src="thumbnail" mode="aspectFill"></image>
        <!-- <image class="add"  src="/static/add.png" mode="aspectFit"></image> -->
        <text class="add" v-else>缩略图</text>
    </view>
</view>
<view class="input-header">
    <input class="input-h" type="text" placeholder="输入标题" v-model="title">
</view>
<view class="editor-box">
<!-- 如果交互的可以之间传提交地址,如果不交互可以不传返回base64,在富文本显示 -->
<!--jinEdit 手机端富文本插件  -->
    <jinEdit placeholder="请输入编辑内容" @editOk="editOk" uploadFileUrl=""></jinEdit> 
</view>

jinEdit组件改写

uni.chooseImage({
    count: 9, //默认9
    sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
    sourceType: ['album', 'camera'], //从相册选择
    success: async(res) => {
        var file = res.tempFiles[0];
            //utils.js 压缩返回base64,直接渲染
            photoCompress(file, {
                quality: 0.3
                }, function(base64Codes){
                //这里可以一个图片预览 把base64Codes 赋值给img的src即可
                console.log(base64Codes);
                _this.editorCtx.insertImage({
                    src: base64Codes,  // 此处需要将图片地址切换成服务器返回的真实图片地址
                    alt: '图片',
                    success: function(e) {}
                });
            })
    }
})

场景应用:手机端新闻发布

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多