美文网首页
springboot2分片上传与极速妙传

springboot2分片上传与极速妙传

作者: IT小池 | 来源:发表于2022-03-15 13:25 被阅读0次

    分片上传本质就是在前端把一个完整的文件拆分成若干份文件上传,上传完成后,服务器再把上传的若干份文件合并成一个完整的文件,再删除若干份分片文件。

    首先导包

    <dependencies>
      <!-- 基本依赖 -->
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <!-- mysql -->
      <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
      </dependency>
      <!-- mapper -->
         <dependency>
         <groupId>tk.mybatis</groupId>
         <artifactId>mapper-spring-boot-starter</artifactId>
         <version>2.1.5</version>
      </dependency>
      <!-- fastjson -->
      <dependency>
         <groupId>com.alibaba</groupId>
         <artifactId>fastjson</artifactId>
         <version>1.2.79</version>
      </dependency>
       <!-- lombok -->
       <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.22</version>
       </dependency>
    </dependencies>
    

    application.yml配置:

    server:
      port: 8080
      servlet:
        context-path: /test
      tomcat:
        max-http-form-post-size: -1
    spring:
      datasource:
        name: DS #如果存在多个数据源,监控的时候可以通过名字来区分开来。如果没有配置,将会生成一个名字,格式是:"DataSource-" + System.identityHashCode(this)
        type: com.zaxxer.hikari.HikariDataSource
        url: jdbc:mysql://127.0.0.1:3306/upload?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT
        #hikari相关配置
        hikari:
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: 123456
      servlet:
        multipart:
          max-file-size: 50MB
          max-request-size: 50MB
    
    # mybatis配置
    mybatis:
      type-aliases-package: com.upload.pojo
    #  mapper-locations: classpath:mapper/*.xml
      configuration:
        map-underscore-to-camel-case: true
        cache-enabled: true #使全局的映射器启用或禁用缓存。
        lazy-loading-enabled: true #全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。
        aggressive-lazy-loading: true #当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。
        jdbc-type-for-null: null #设置但JDBC类型为空时,某些驱动程序 要指定值,default:OTHER,插入空值时不需要指定类型
    
    logging:
      level:
       com.upload.dao: debug
    

    再数据库建一张表 file

    CREATE TABLE `file` (
      `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
      `path` varchar(255) NOT NULL DEFAULT '' COMMENT '文件路径',
      `name` varchar(255) NOT NULL DEFAULT '' COMMENT '文件名称',
      `size` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '文件大小',
      `suffix` varchar(10) NOT NULL DEFAULT '' COMMENT '后缀',
      `type` varchar(10) NOT NULL DEFAULT '' COMMENT '文件类型',
      `share_total` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '文件分片总数',
      `share_index` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '已上传分片索引,默认0',
      `key` varchar(32) NOT NULL DEFAULT '' COMMENT '文件唯一Key',
      `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
      `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
      PRIMARY KEY (`id`),
      UNIQUE KEY `key` (`key`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='文件分片上传表';
    

    创建对应实体类 FilePojo

    package com.upload.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import javax.persistence.Column;
    import javax.persistence.Id;
    import javax.persistence.Table;
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     * @author xiaochi
     * @date 2022/3/14 22:52
     * @desc FilePojo
     */
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Table(name = "file")
    public class FilePojo implements Serializable {
        private static final long serialVersionUID = -6334172193008858856L;
    
        @Id
        private Integer id;
        private String path;
        private String name;
        private Long size;
        private String suffix;
        @Column(name = "`type`")
        private String type;
        private Integer shareTotal;
        private Integer shareIndex;
        @Column(name = "`key`")
        private String key;
        private Date createTime;
        private Date updateTime;
    }
    

    接着再创建一个接收前端参数的Vo文件 FileVo

    package com.upload.vo;
    
    import com.upload.pojo.FilePojo;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    import org.springframework.web.multipart.MultipartFile;
    
    /**
     * @author xiaochi
     * @date 2022/3/15 8:38
     * @desc FileVo
     */
    @Data
    @EqualsAndHashCode(callSuper = true)
    public class FileVo extends FilePojo {
    
        private static final long serialVersionUID = -4528742454491886780L;
    
        private MultipartFile file;
    }
    

    FileVo拥有 FilePojo 的所有属性。接着创建一个 FileDao文件

    /**
     * @author xiaochi
     * @date 2022/3/14 22:56
     * @desc FileDao
     */
    public interface FileDao extends Mapper<FilePojo>, MySqlMapper<FilePojo> {
    }
    

    接着创建控制器 UploadController

    package com.upload.controller;
    
    import com.upload.common.R;
    import com.upload.dao.FileDao;
    import com.upload.pojo.FilePojo;
    import com.upload.vo.FileVo;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.BeanUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    import tk.mybatis.mapper.entity.Example;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * @author xiaochi
     * @date 2022/3/14 22:08
     * @desc UploadController
     */
    @Slf4j
    @CrossOrigin("*")
    @RestController
    @RequestMapping("/file")
    public class UploadController {
    
        private static final String FILE_PATH = "d:/upload/";
    
        @Autowired
        private FileDao fileDao;
    
        /**
         * 根据文件唯一key判断是否有上传
         * @param key
         * @return
         */
        @GetMapping("/check/{key}")
        public R<FilePojo> check(@PathVariable String key){
            return R.ok(findByKey(key));
        }
    
        /**
         * 分片上传(表单接收)
         * @param fileVo
         * @return
         * @throws Exception
         */
        @PostMapping(value = "/upload")
    //    public R<String> upload(@RequestBody @RequestParam("file") MultipartFile file) throws IOException {
        public R<String> upload(FileVo fileVo) throws Exception {// 表单接收
            MultipartFile file = fileVo.getFile();
            String filename = file.getOriginalFilename();
            String date  = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
            String localPath = new StringBuilder()
                    .append(date)
                    .append(File.separator)
                    .append(fileVo.getName())
                    .append(".")
                    .append(fileVo.getShareIndex())
                    .toString();// 分片文件路径与后缀处理 2022/03/15\13-提交Git仓库.mp4.0 、2022/03/15\13-提交Git仓库.mp4.1、2022/03/15\13-提交Git仓库.mp4.2 .....
            fileVo.setPath(localPath);
            File dest = new File(FILE_PATH + localPath);
            if (!dest.getParentFile().exists()){
                dest.getParentFile().setWritable(true);
                dest.getParentFile().mkdirs();// 不加 getParentFile() 创建的是文件夹,不是文件
            }
            file.transferTo(dest);
            FilePojo filePojo = new FilePojo();
            BeanUtils.copyProperties(fileVo,filePojo);
            String path = new StringBuilder()
                    .append(date)
                    .append(File.separator)
                    .append(fileVo.getName())
                    .toString();// 数据库保存的最后完整文件的路径与名称, 2022/03/15\13-提交Git仓库.mp4
            filePojo.setPath(path);
    
            // 查询之前是否有过上传
            Example example = new Example(FilePojo.class);
            Example.Criteria criteria = example.createCriteria();
            criteria.andEqualTo("key",filePojo.getKey());
            FilePojo filePojoDb = fileDao.selectOneByExample(example);
            if (filePojoDb == null){
                fileDao.insertSelective(filePojo);
            }else {
                fileDao.updateByExampleSelective(filePojo,example);
            }
    
            // 判断是否上传玩最后一个分片文件,然后进行合并完整文件并删除所有分片文件
            if (fileVo.getShareIndex().equals(fileVo.getShareTotal()-1)){
                this.merge(filePojo);
            }
            return R.ok(path);
        }
    
        /**
         * 合并所有分片文件成功后并删除所有分片文件
         * @param filePojo
         * @throws Exception
         */
        private void merge(FilePojo filePojo) throws Exception {
            String path = FILE_PATH + filePojo.getPath();
            //FileOutputStream outputStream = new FileOutputStream(new File(path), true);// true表示可追加
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(path), true));
    
            //FileInputStream inputStream = null;
            BufferedInputStream bis = null;
            byte[] byt = new byte[10* 1024 * 1024];
            int len;
    
            try {
                for (int i = 0,leng = filePojo.getShareTotal(); i < leng; i++) {
                    // 从第i个分片读取
                    /*inputStream = new FileInputStream(new File(path + "." + i));//
                    while ((len = inputStream.read(byt)) != -1){
                        outputStream.write(byt,0,len);
                    }*/
                    bis = new BufferedInputStream(new FileInputStream(new File(path + "." + i)));
                    while ((len = bis.read(byt))!= -1){
                        bos.write(byt,0,len);
                    }
                }
            }catch (IOException e){
                log.error("分片合并异常", e);
            }finally {
                /*try {
                    if (inputStream != null){
                        inputStream.close();
                    }
                    outputStream.close();
                }catch (Exception e){
                    log.error("IO流关闭异常", e);
                }*/
                bos.flush();
                bos.close();
                if (bis != null){
                    bis.close();
                }
            }
    
            // 删除分片文件
            System.gc();
            Thread.sleep(100);
    
            for (int i = 0,leng = filePojo.getShareTotal(); i < leng; i++) {
                String localPath = path + "." + i;
                File file = new File(localPath);
                if (file.exists()){
                    boolean result = file.delete();
                    log.info("删除分片文件{},{}", localPath, result ? "成功" : "失败");
                }
            }
        }
    
        /**
         * 根据文件唯一key查询
         * @param key
         * @return
         */
        private FilePojo findByKey(String key){
            Example example = new Example(FilePojo.class);
            Example.Criteria criteria = example.createCriteria();
            criteria.andEqualTo("key",key);
            return fileDao.selectOneByExample(example);
        }
    }
    

    然后前端对应的请求代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div>
        上传:
        <input type="file" id="file">
    </div>
    <button id="btn">点击上传</button>
    <script src="https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.19.0/js/md5.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script>
        $(function () {
            $("#btn").click(function () {
                var file = $("#file").prop("files")[0];
                if (file){
                    var size = file.size;
                    var shareSize = 10 * 1024 * 1024;// 默认分片大小 20M
                    var shareIndex = 0;// 默认从第0片开始
                    var shareTotal = Math.ceil(size / shareSize);// 计算总分片
    
                    var key = md5(file.name+file.type+file.size);
    
                    var params = {
                        filename:file.name,
                        size:size,
                        suffix:file.name.substring(file.name.lastIndexOf(".")+1),
                        type:file.type,
                        shareTotal:shareTotal,
                        shareIndex:shareIndex,
                        key:key,
                    };
    
                    // 先查询是否有过上传,或 上传到哪一个的索引
                    $.ajax({
                        type: "GET", // 数据提交类型
                        url: "http://localhost:8080/test/file/check/"+key, // 发送地址
                        dataType:"json",
                        success:function (res) {
                            if (res && res.data){
                                // 存在说明之前上传过,接着判断是否之前上传完成
                                if (res.data.shareIndex === (res.data.shareTotal - 1)){
                                    // 相等说之前已经上传完整
                                    alert("极速秒传成功");
                                }else{
                                    // 不相等说明之前上传中断了,接着再上传
                                    upload(params,file,res.data.shareIndex,shareSize);
                                }
                            }else{
                                // 没有穿过就从第0个分片开始上传
                                upload(params,file,shareIndex,shareSize);
                            }
                        }
                    });
                }
            })
        });
    
        /**
         * 上传
         * @param params
         * @param file
         * @param shareIndex
         * @param shareSize
         */
        function upload(params,file,shareIndex,shareSize) {
            var start = shareIndex * shareSize;// 分片起始位置
            var end = Math.min(params.size,start+shareSize);
            var fileShare = file.slice(start,end);// 截取file文件分片,进行上传
    
            var formData = new FormData();
            formData.append("file",fileShare);
            formData.append("name",params.filename);
            formData.append("size",params.size);
            formData.append("suffix",params.suffix);
            formData.append("type",params.type);
            formData.append("shareTotal",params.shareTotal);
            formData.append("shareIndex",shareIndex);
            formData.append("key",params.key);
    
            $.ajax({
                type: "POST", // 数据提交类型
                url: "http://localhost:8080/test/file/upload", // 发送地址
                dataType:"json",
                data: formData, //发送数据
                // async: true, // 是否异步
                processData: false,
                contentType: false, // 注意:此处并不是json传输数据,而是表单
                success:function (res) {
                    if (shareIndex === (params.shareTotal - 1)){
                        // 分片文件已上传完
                        return;
                    }else{
                        // 递归上传分片文件
                        upload(params,file,shareIndex+1,shareSize);
                    }
                }
            })
        }
    </script>
    </body>
    </html>
    

    到此完成,但是这里后台接口没有做文件完整性验证,如要进行完整性验证,请看下面

    文件完整性验证上传

    要进行文件完整性验证就不能直接 MultipartFile接收了,要在前端吧文件转成 Base64 进行上传了,然后后台接收后解析成 MultipartFile

    首先,FileVo文件要修改下

    package com.upload.vo;
    
    import com.upload.pojo.FilePojo;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    import org.springframework.web.multipart.MultipartFile;
    
    /**
     * @author xiaochi
     * @date 2022/3/15 8:38
     * @desc FileVo
     */
    @Data
    @EqualsAndHashCode(callSuper = true)
    public class FileVo extends FilePojo {
    
        private static final long serialVersionUID = -4528742454491886780L;
    
        private String file;// base64文件字符串
        private String encryFile;// 前端进行md5加密后的符
    }
    

    接着新建一个文件 Base64DecodeMultipartFile 用来将接收到的Base64字符串转成 MultipartFile

    package com.upload.util;
    
    import org.springframework.web.multipart.MultipartFile;
    import sun.misc.BASE64Decoder;
    
    import java.io.ByteArrayInputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    /** base64转为multipartFile
     * @author xiaochi
     * @date 2022/4/11 15:30
     * @desc Base64DecodeMultipartFile
     */
    public class Base64DecodeMultipartFile implements MultipartFile {
        private final byte[] imgContent;
    
        private final String header;
    
        public Base64DecodeMultipartFile(byte[] imgContent, String header) {
            this.imgContent = imgContent;
            this.header = header.split(";")[0];
        }
    
        @Override
        public String getName() {
            return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1];
        }
    
        @Override
        public String getOriginalFilename() {
            return System.currentTimeMillis() + (int) Math.random() * 10000 + "." + header.split("/")[1];
        }
    
        @Override
        public String getContentType() {
            return header.split(":")[1];
        }
    
        @Override
        public boolean isEmpty() {
            return imgContent == null || imgContent.length == 0;
        }
    
        @Override
        public long getSize() {
            return imgContent.length;
        }
    
        @Override
        public byte[] getBytes() throws IOException {
            return imgContent;
        }
    
        @Override
        public InputStream getInputStream() throws IOException {
            return new ByteArrayInputStream(imgContent);
        }
    
        @Override
        public void transferTo(File dest) throws IOException, IllegalStateException {
            new FileOutputStream(dest).write(imgContent);
        }
    
        /**
         *  * base64转multipartFile
         *  * @param base64
         *  * @return
         */
        public static MultipartFile base64Convert(String base64) {
            String[] baseStrs = base64.split(",");
            BASE64Decoder decoder = new BASE64Decoder();
            byte[] b = new byte[0];
            try {
                b = decoder.decodeBuffer(baseStrs[1]);
            } catch (IOException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < b.length; ++i) {
                if (b[i] < 0) {
                    b[i] += 256;
                }
            }
            return new Base64DecodeMultipartFile(b, baseStrs[0]);
        }
    }
    

    接着修改上传文件的接口控制器方法 UploadController的上传方法 upload

    /**
     * 分片上传(表单接收),且进行文件完整性验证
     * @param fileVo
     * @return
     * @throws Exception
     */
    @PostMapping("/upload")
    public R<String> upload(FileVo fileVo) throws Exception {
        MultipartFile file = Base64DecodeMultipartFile.base64Convert(fileVo.getFile());// 将 Base64 字符串解析成 MultipartFile
        // 验证文件完整性
        if (!Objects.equals(fileVo.getEncryFile(),DigestUtils.md5DigestAsHex(fileVo.getFile().getBytes()))){
            return R.error("上传文件已被损坏");
        }
        String date  = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
        String localPath = new StringBuilder()
                .append(date)
                .append(File.separator)
                .append(fileVo.getName())
                .append(".")
                .append(fileVo.getShareIndex())
                .toString();// 分片文件路径与后缀处理 2022/03/15\13-提交Git仓库.mp4.0 、2022/03/15\13-提交Git仓库.mp4.1、2022/03/15\13-提交Git仓库.mp4.2 .....
        fileVo.setPath(localPath);
        File dest = new File(FILE_PATH + localPath);
        if (!dest.getParentFile().exists()){
            dest.getParentFile().setWritable(true);
            dest.getParentFile().mkdirs();// 不加 getParentFile() 创建的是文件夹,不是文件
        }
        file.transferTo(dest);
        FilePojo filePojo = new FilePojo();
        BeanUtils.copyProperties(fileVo,filePojo);
        String path = new StringBuilder()
                .append(date)
                .append(File.separator)
                .append(fileVo.getName())
                .toString();// 数据库保存的最后完整文件的路径与名称, 2022/03/15\13-提交Git仓库.mp4
        filePojo.setPath(path);
    
        // 查询之前是否有过上传
        Example example = new Example(FilePojo.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("key",filePojo.getKey());
        FilePojo filePojoDb = fileDao.selectOneByExample(example);
        if (filePojoDb == null){
            fileDao.insertSelective(filePojo);
        }else {
            fileDao.updateByExampleSelective(filePojo,example);
        }
    
        // 判断是否上传玩最后一个分片文件,然后进行合并完整文件并删除所有分片文件
        if (fileVo.getShareIndex().equals(fileVo.getShareTotal()-1)){
            this.merge(filePojo);
        }
        return R.ok(path);
    }
    

    接着修改对应的前端代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div>
        上传:
        <input type="file" id="file">
    </div>
    <button id="btn">点击上传</button>
    <script src="https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.19.0/js/md5.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script>
        $(function () {
            $("#btn").click(function () {
                var file = $("#file").prop("files")[0];
                if (file){
                    var size = file.size;
                    var shareSize = 10 * 1024 * 1024;// 默认分片大小 20M
                    var shareIndex = 0;// 默认从第0片开始
                    var shareTotal = Math.ceil(size / shareSize);// 计算总分片
    
                    var key = md5(file.name+file.type+file.size);
    
                    var params = {
                        filename:file.name,
                        size:size,
                        suffix:file.name.substring(file.name.lastIndexOf(".")+1),
                        type:file.type,
                        shareTotal:shareTotal,
                        shareIndex:shareIndex,
                        key:key,
                    };
    
                    // 先查询是否有过上传,或 上传到哪一个的索引
                    $.ajax({
                        type: "GET", // 数据提交类型
                        url: "http://localhost:8080/test/file/check/"+key, // 发送地址
                        dataType:"json",
                        success:function (res) {
                            if (res && res.data){
                                // 存在说明之前上传过,接着判断是否之前上传完成
                                if (res.data.shareIndex === (res.data.shareTotal - 1)){
                                    // 相等说之前已经上传完整
                                    alert("极速秒传成功");
                                }else{
                                    // 不相等说明之前上传中断了,接着再上传
                                    upload(params,file,res.data.shareIndex,shareSize);
                                }
                            }else{
                                // 没有穿过就从第0个分片开始上传
                                upload(params,file,shareIndex,shareSize);
                            }
                        }
                    });
                }
            })
        });
    
        /**
         * 上传
         * @param params
         * @param file
         * @param shareIndex
         * @param shareSize
         */
        function upload(params,file,shareIndex,shareSize) {
            var start = shareIndex * shareSize;// 分片起始位置
            var end = Math.min(params.size,start+shareSize);
            var fileShare = file.slice(start,end);// 截取file文件分片,进行上传
            // base64 上传且进行文件完整性验证
            fileToBase64(fileShare, function(base64){
                var formData = new FormData();
                formData.append("file",base64);
                formData.append("encryFile",md5(base64));// 用于验证文件完整性
                formData.append("name",params.filename);
                formData.append("size",params.size);
                formData.append("suffix",params.suffix);
                formData.append("type",params.type);
                formData.append("shareTotal",params.shareTotal);
                formData.append("shareIndex",shareIndex);
                formData.append("key",params.key);
    
                $.ajax({
                    type: "POST", // 数据提交类型
                    url: "http://localhost:8080/test/file/upload", // 发送地址
                    dataType:"json",
                    data: formData, //发送数据
                    // async: true, // 是否异步
                    processData: false,
                    contentType: false, // 注意:此处并不是json传输数据,而是表单
                    success:function (res) {
                        // res.code = 1,表示上传失败
                        if (res.code == 1 || shareIndex === (params.shareTotal - 1)){
                            // 分片文件已上传完
                            return;
                        }else{
                            // 递归上传分片文件
                            upload(params,file,shareIndex+1,shareSize);
                        }
                    }
                })
            })
        }
        
        /**
         * File 转 Base64 图片
         */
        function fileToBase64(file, callback){
            const reader = new FileReader()
            reader.onload = function(evt){
               if(typeof callback === 'function') {
                    callback(evt.target.result)
                } else {
                    console.log("我是base64:", evt.target.result);
                }
            }
            /* readAsDataURL 方法会读取指定的 Blob 或 File 对象
            ** 读取操作完成的时候,会触发 onload 事件
            *  result 属性将包含一个data:URL格式的字符串(base64编码)以表示所读取文件的内容。
            */ 
            reader.readAsDataURL(file);
        }
    </script>
    </body>
    </html>
    

    然后运行上传完整,ok,到此结束。

    相关文章

      网友评论

          本文标题:springboot2分片上传与极速妙传

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