国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

spring-boot中實現(xiàn)分片上傳文件

這篇具有很好參考價值的文章主要介紹了spring-boot中實現(xiàn)分片上傳文件。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一、上傳文件基本實現(xiàn)

  • 1、前端效果圖展示,這里使用element-ui plus來展示樣式效果

spring-boot中實現(xiàn)分片上傳文件,java開發(fā),spring boot

  • 2、基礎(chǔ)代碼如下

    <template>
      <div
        >
        <el-upload
          ref="uploadRef"
          class="upload-demo"
          :limit="1"
          :on-change="handleExceed"
          :auto-upload="false"
        >
          <template #trigger>
            <el-button type="primary">選擇文件</el-button>
          </template>
          <el-button style="margin-left: 30px" type="success" @click="submitUpload"> 上傳 </el-button>
        </el-upload>
      </div>
    </template>
    
    <script setup>
      import { ref } from 'vue';
    
      import axios from 'axios';
    
      const fileRef = ref(null);
    
      const handleExceed = (files) => {
        console.log(files);
        fileRef.value = files;
      };
    
      const submitUpload = () => {
        console.log('開始上傳文件', JSON.stringify(fileRef.value));
        const formData = new FormData();
        formData.append('file', fileRef.value.raw);
        axios.post('http://localhost:9002/file/upload', formData).then((res) => {
          console.log(fileRef.value, '??');
          console.log('上傳成功');
        });
      };
    </script>
    
    <style lang="scss" scoped></style>
    
    
  • 3、定義后端接口,并且處理好跨域(關(guān)于跨域處理,自己百度處理)

    package com.course.file.controller;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    @RestController
    public class UploadController {
        private static final Logger LOG = LoggerFactory.getLogger(UploadController.class);
        @PostMapping("/upload")
        public String uploadApi(@RequestParam MultipartFile file) {
            this.LOG.info("上傳文件開始");
            this.LOG.info(file.getOriginalFilename());
            this.LOG.info(String.valueOf(file.getSize()));
            return "上傳成功";
        }
    }
    
  • 4、保存文件到本地文件

    package com.course.file.controller;
    
    import com.course.file.utils.UuidUtil;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.File;
    import java.io.IOException;
    
    @RestController
    public class UploadController {
        private static final Logger LOG = LoggerFactory.getLogger(UploadController.class);
    
        @PostMapping("/upload")
        public String uploadApi(@RequestParam MultipartFile file) throws IOException {
            this.LOG.info("上傳文件開始");
            this.LOG.info(file.getOriginalFilename());
            this.LOG.info(String.valueOf(file.getSize()));
            // 保存文件到本地
            String fileName = file.getOriginalFilename();
            String key = UuidUtil.getShortUuid();
            // 需要先本地創(chuàng)建一個file文件夾
            String fullPath = "E:/file/" + key + "-" + fileName;
            File dest = new File(fullPath);
            file.transferTo(dest);
            return "上傳成功";
        }
    }
    

二、配置靜態(tài)目錄

  • 1、在FileApplication.java旁邊添加一個SpringMvcConfig.java的文件

    package com.course.file;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class SpringMvcConfig implements WebMvcConfigurer {
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/f/**").addResourceLocations("file:E:/file/");
        }
        // http://localhost:9002/file/f/FdXMQJdF-xx.png
    }
    
  • 2、直接瀏覽器上直接訪問上面的地址

  • 3、上傳地址返回給前端

    @RestController
    public class UploadController {
        private static final Logger LOG = LoggerFactory.getLogger(UploadController.class);
    
        @PostMapping("/upload")
        public String uploadApi(@RequestParam MultipartFile file) throws IOException {
            this.LOG.info("上傳文件開始");
            this.LOG.info(file.getOriginalFilename());
            this.LOG.info(String.valueOf(file.getSize()));
            // 保存文件到本地
            String fileName = file.getOriginalFilename();
            String key = UuidUtil.getShortUuid();
            // 需要先本地創(chuàng)建一個file文件夾
            String fullPath = "E:/file/" + key + "-" + fileName;
            File dest = new File(fullPath);
            file.transferTo(dest);
            return "http://localhost:9002/file/f/" + key + "-" + fileName;
        }
    }
    
  • 4、將上面幾個固定的配置寫到配置文件中

    file.path=E:/file/
    file.domain=http://localhost:9002/file/
    # 修改上傳文件大小(不設(shè)置大文件可能上傳失敗)
    spring.servlet.multipart.max-file-size=50MB
    spring.servlet.multipart.max-request-size=50MB
    
  • 5、在控制器中使用

    public class UploadController {
        private static final Logger LOG = LoggerFactory.getLogger(UploadController.class);
        @Value("${file.path}")
        private String FILE_PATH;
    
        @Value("${file.domain}")
        private String FILE_DOMAIN;
    }
    

三、斷點續(xù)傳

  • 1、主要原理

    • 前端將大文件根據(jù)文件大小來切割成小片段,使用遞歸的方式調(diào)用后端接口,將文件上傳到服務(wù)器端
    • 服務(wù)器端接收到前端上傳片段,存儲到服務(wù)器上
    • 等前端最后一個上傳完成后,將全部的文件合并成一個文件
    • 合并完成后,返回一個url地址,將之前的分片上傳的文件刪除
  • 2、手動演示前端分段上傳

    const submitUpload = () => {
        console.log('開始上傳文件', JSON.stringify(fileRef.value));
        const formData = new FormData();
        const file = fileRef.value.raw;
        // 文件分配
        let shardSize = 5 * 1024 * 1024; // 以5MB為一個分片
        let shardIndex = 0; // 分片索引
        let start = shardIndex * shardSize; // 開始位置
        let end = Math.min(file.size, start + shardSize); // 結(jié)束位置
        let fileShard = file.slice(start, end); // 每次上傳的分片數(shù)據(jù)
        formData.append('file', fileShard);
        axios.post('http://localhost:9002/file/upload', formData).then((res) => {
          console.log(fileRef.value, '??');
          console.log('上傳成功');
        });
      };
    
  • 3、第二次的時候?qū)?code>shardIndex改為1

  • 4、查看本地文件夾下的文件

    spring-boot中實現(xiàn)分片上傳文件,java開發(fā),spring boot

  • 5、手動創(chuàng)建一個接口來嘗試合并文件

    @GetMapping("merge")
        public String merge() throws FileNotFoundException {
            // 最終合成后的視頻文件名稱
            File newFile = new File(FILE_PATH + "test.mp4");
            FileOutputStream outputStream = new FileOutputStream(newFile, true);
            FileInputStream fileInputStream = null;
            byte[] bytes = new byte[5 * 1024 * 1024];
            int len;
            try {
                // 讀取第一段
                fileInputStream = new FileInputStream(new File(FILE_PATH + "/pN0EoOny-blob"));
                while ((len = fileInputStream.read(bytes)) != -1) {
                    outputStream.write(bytes, 0, len);
                }
                // 讀取第二段
                fileInputStream = new FileInputStream(new File(FILE_PATH + "/f5oeIEDW-blob"));
                while ((len = fileInputStream.read(bytes)) != -1) {
                    outputStream.write(bytes, 0, len);
                }
                // 讀取第三段
                fileInputStream = new FileInputStream(new File(FILE_PATH + "/qsm8n03q-blob"));
                while ((len = fileInputStream.read(bytes)) != -1) {
                    outputStream.write(bytes, 0, len);
                }
            } catch (IOException e) {
                LOG.error("合并分片失敗", e);
            } finally {
                try {
                    if (fileInputStream != null) {
                        fileInputStream.close();
                    }
                    outputStream.close();
                    LOG.info("IO流關(guān)閉");
                } catch (IOException e) {
                    LOG.error("IO流關(guān)閉", e);
                }
            }
            return "合并視頻成功";
        }
    

四、使用數(shù)據(jù)庫來實現(xiàn)分片上傳

  • 1、數(shù)據(jù)表字段

    在數(shù)據(jù)庫中涉及key只跟文件有關(guān),跟上傳多少片沒關(guān)系的,當已經(jīng)上傳的分片數(shù)和分片總數(shù)一樣的時候就合并文件

    drop table if exists `file`;
    create table `file` (
      `id` int(11) not null PRIMARY key auto_increment comment '主鍵id',
      `path` varchar(100) not null comment '相對路徑',
      `name` varchar(100) comment '文件名',
      `suffix` varchar(10) comment '后綴',
      `size` int(11) comment '大小|字節(jié)B',
    	`shard_index` int(11) DEFAULT 0 COMMENT '已上傳分片',
    	`shard_size` int(11) DEFAULT 0 COMMENT '分片大小',
    	`shard_total` int(11) DEFAULT 0 COMMENT '分片總數(shù)',
    	`key` VARCHAR(100) DEFAULT NULL COMMENT '文件標識',
      `created_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '創(chuàng)建時間',
      `updated_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新時間',
      `deleted_at` timestamp(6) NULL DEFAULT NULL COMMENT '軟刪除時間'
    ) engine=innodb default charset=utf8mb4 comment='文件';
    
  • 2、在pom.xml文件中添加mybatis-plus的依賴包

    <!--    配置連接到數(shù)據(jù)庫    -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.26</version>
    </dependency>
    <!--   mybatis plus 依賴包  -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.2.0</version>
    </dependency>
    
    <!--    mybatis 代碼生成器    -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.2.0</version>
    </dependency>
    
  • 3、application.properties添加配置

    # mysql數(shù)據(jù)庫鏈接
    spring.datasource.url=jdbc:mysql://localhost:3306/beego?characterEncoding=utf-8&serverTimezone=GMT%2B8
    spring.datasource.username=root
    spring.datasource.password=123456
    
    # 配置mybatis-plus
    # 開啟下劃線轉(zhuǎn)駝峰
    mybatis-plus.configuration.map-underscore-to-camel-case=true 
    mybatis-plus.configuration.auto-mapping-behavior=full
    mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    # mapping的路徑
    mybatis-plus.mapper-locations=classpath*:mapper/**/*Mapper.xml
    #主鍵類型  AUTO:"數(shù)據(jù)庫ID自增", INPUT:"用戶輸入ID", ID_WORKER:"全局唯一ID (數(shù)字類型唯一ID)", UUID:"全局唯一ID UUID";
    mybatis-plus.global-config.db-config.id-type=AUTO
    # 邏輯刪除(軟刪除)
    mybatis-plus.global-config.db-config.logic-delete-value=NOW()
    mybatis-plus.global-config.db-config.logic-not-delete-value=NULL
    
  • 4、使用模板生成器生成代碼

  • 5、前端使用遞歸的方式來分片上傳文件

    <script setup>
      import { ref } from 'vue';
      import { genFileId } from 'element-plus';
      import axios from 'axios';
      import md5 from 'js-md5';
    
      const fileRef = ref(null);
    
      const handleExceed = (files) => {
        console.log(files);
        fileRef.value = files;
      };
    
      const updateFile = (shardIndex) => {
        const formData = new FormData();
        const file = fileRef.value.raw;
        // 文件分配
        let shardSize = 5 * 1024 * 1024; // 以5MB為一個分片
        // let shardIndex = 0; // 分片索引
        let start = (shardIndex - 1) * shardSize; // 開始位置
        let end = Math.min(file.size, start + shardSize); // 結(jié)束位置
        let fileShard = file.slice(start, end); // 每次上傳的分片數(shù)據(jù)
        // 前端多上傳參數(shù)
        let size = file.size;
        let shardTotal = Math.ceil(size / shardSize); // 總片數(shù)
        const suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
        formData.append('shard', fileShard);
        formData.append('shardIndex', shardIndex);
        formData.append('shardSize', shardSize);
        formData.append('shardTotal', shardTotal);
        formData.append('name', file.name);
        formData.append('size', size);
        formData.append('suffix', suffix); 
        formData.append('key', md5(`${file.name}_${file.size}_${file.type}`));
    
        axios.post('http://localhost:9002/file/upload1', formData).then((res) => {
          // 判斷如果當前的shardIndex < shardTotal的時候遞歸上傳
          if (shardIndex < shardTotal) {
            updateFile(++shardIndex);
          } else {
            console.log('上傳成功');
          }
        });
      };
      const submitUpload = () => {
        console.log('開始上傳文件', JSON.stringify(fileRef.value));
        // 開始上傳文件
        updateFile(1);
      };
    </script>
    
  • 6、后端對文件上傳處理,存儲到本地和入庫操作

    package com.course.file.controller;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.course.file.model.FileEntity;
    import com.course.file.service.IFileService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.File;
    import java.io.IOException;
    
    @RestController
    public class FileController {
        @Autowired
        private IFileService fileService;
        private static final Logger LOG = LoggerFactory.getLogger(FileController.class);
        @Value("${file.path}")
        private String FILE_PATH;
    
        @Value("${file.domain}")
        private String FILE_DOMAIN;
    
        @PostMapping("upload1")
        public String upload1(
                @RequestParam MultipartFile shard,
                Integer shardIndex,
                Integer shardSize,
                Integer shardTotal,
                String name,
                String suffix,
                Integer size,
                String key
        ) throws IOException {
            this.LOG.info("開始上傳文件");
            System.out.println("當前分片:" + shardIndex);
            System.out.println("當前分片大小:" + shardSize);
            System.out.println("當前分片總數(shù):" + shardTotal);
            System.out.println("文件名稱:" + name);
            System.out.println("文件后綴名:" + suffix);
            System.out.println("文件大小:" + size);
            System.out.println("文件唯一的key:" + key);
            // 文件保存到本地目錄下
            String localPath = this.FILE_PATH + key + "." + suffix + "." + shardIndex;
            File dest = new File(localPath);
            shard.transferTo(dest);
            LOG.info(dest.getAbsolutePath());
            // 數(shù)據(jù)入庫操作
            LambdaQueryWrapper<FileEntity> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(FileEntity::getFileKey, key);
            FileEntity fileEntity = new FileEntity();
            if (this.fileService.getOne(queryWrapper) != null) {
                // 說明不是第一次上傳,需要更新當前上傳的分片數(shù)量
                fileEntity.setShardIndex(shardIndex);
                if (this.fileService.update(fileEntity, queryWrapper)) {
                    return "上傳成功";
                } else {
                    return "上傳失敗";
                }
            } else {
                // 第一次上傳創(chuàng)建
                String path = this.FILE_PATH + key + "." + suffix;
                fileEntity.setFileKey(key);
                fileEntity.setName(name);
                fileEntity.setPath(path);
                fileEntity.setShardIndex(shardIndex);
                fileEntity.setSuffix(suffix);
                fileEntity.setShardSize(shardSize);
                fileEntity.setShardTotal(shardTotal);
                fileEntity.setSize(size);
                if (this.fileService.save(fileEntity)) {
                    return "上傳成功";
                } else {
                    return "上傳失敗";
                }
            }
        }
    }
    
  • 7、查看本地目錄是否生成分片文件

    spring-boot中實現(xiàn)分片上傳文件,java開發(fā),spring boot

  • 8、對本地分片文件合并操作,當shardIndex=shardTotal的時候進行合并操作

    @RestController
    public class FileController {
        @Autowired
        private IFileService fileService;
        private static final Logger LOG = LoggerFactory.getLogger(FileController.class);
        @Value("${file.path}")
        private String FILE_PATH;
    
        @Value("${file.domain}")
        private String FILE_DOMAIN;
    
        @PostMapping("upload1")
        public String upload1(
                @RequestParam MultipartFile shard,
                Integer shardIndex,
                Integer shardSize,
                Integer shardTotal,
                String name,
                String suffix,
                Integer size,
                String key
        ) throws IOException {
            this.LOG.info("開始上傳文件");
            System.out.println("當前分片:" + shardIndex);
            System.out.println("當前分片大小:" + shardSize);
            System.out.println("當前分片總數(shù):" + shardTotal);
            System.out.println("文件名稱:" + name);
            System.out.println("文件后綴名:" + suffix);
            System.out.println("文件大小:" + size);
            System.out.println("文件唯一的key:" + key);
            // 文件保存到本地目錄下
            String localPath = this.FILE_PATH + key + "." + suffix + "." + shardIndex;
            File dest = new File(localPath);
            shard.transferTo(dest);
            LOG.info(dest.getAbsolutePath());
            // 合并文件操作
            if (Objects.equals(shardIndex, shardTotal)) {
                this.merge(key, suffix, shardTotal);
            }
    
            // 數(shù)據(jù)入庫操作
            LambdaQueryWrapper<FileEntity> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(FileEntity::getFileKey, key);
            FileEntity fileEntity = new FileEntity();
            if (this.fileService.getOne(queryWrapper) != null) {
                // 說明不是第一次上傳,需要更新當前上傳的分片數(shù)量
                fileEntity.setShardIndex(shardIndex);
    
                if (this.fileService.update(fileEntity, queryWrapper)) {
                    return "上傳成功";
                } else {
                    return "上傳失敗";
                }
            } else {
                // 第一次上傳創(chuàng)建
                String path = this.FILE_PATH + key + "." + suffix;
                fileEntity.setFileKey(key);
                fileEntity.setName(name);
                fileEntity.setPath(path);
                fileEntity.setShardIndex(shardIndex);
                fileEntity.setSuffix(suffix);
                fileEntity.setShardSize(shardSize);
                fileEntity.setShardTotal(shardTotal);
                fileEntity.setSize(size);
                if (this.fileService.save(fileEntity)) {
                    return "上傳成功";
                } else {
                    return "上傳失敗";
                }
            }
        }
    	/**
         * 對上傳的文件片段合并操作
         * @param key
         * @param suffix
         * @param shardTotal
         * @throws FileNotFoundException
         */
        private void merge(String key, String suffix, Integer shardTotal) throws FileNotFoundException {
            this.LOG.info("=====開始合并切片操作=====");
            String path = this.FILE_PATH + key + "." + suffix;
            File newFile = new File(path);
            FileOutputStream outputStream = new FileOutputStream(newFile, true);//文件追加寫入
            FileInputStream fileInputStream = null;//分片文件
            byte[] byt = new byte[10 * 1024 * 1024];
            int len;
            try {
                for (int i = 0; i < shardTotal; i++) {
                    fileInputStream = new FileInputStream(new File(this.FILE_PATH + key + "." + suffix + "." + (i + 1)));
                    while ((len = fileInputStream.read(byt)) != -1) {
                        outputStream.write(byt, 0, len);
                    }
                }
            } catch (Exception e) {
                this.LOG.error("分片合并異常", e);
            } finally {
                try {
                    if (fileInputStream != null) {
                        fileInputStream.close();
                    }
                    outputStream.close();
                    LOG.info("IO流關(guān)閉");
                } catch (Exception e) {
                    LOG.error("IO流關(guān)閉", e);
                }
            }
            this.LOG.info("=====結(jié)束合并切片操作=====");
        }
    }
    
  • 9、當合并后可以對之前的分片進行刪除操作,避免占用更的磁盤空間

    private void merge(String key, String suffix, Integer shardTotal) throws FileNotFoundException, InterruptedException {
        // ...
        this.LOG.info("=====結(jié)束合并切片操作=====");
        System.gc();
        Thread.sleep(1000);
    
        // 刪除分片
        LOG.info("刪除分片開始");
        for (int i = 0; i < shardTotal; i++) {
            String filePath = path + "." + (i + 1);
            File file = new File(filePath);
            boolean result = file.delete();
            this.LOG.info("刪除{},{}", filePath, result ? "成功" : "失敗");
        }
        LOG.info("刪除分片結(jié)束");
    }
    

五、前端使用BS64提交數(shù)據(jù)

  • 1、使用bs64提交后端可以直接定義一個字符串接收數(shù)據(jù),將接收到的數(shù)據(jù)轉(zhuǎn)換為圖片

  • 2、前端將上傳的文件轉(zhuǎn)換為bs64

    // 使用BS64上傳
      const bs64UploadHandler = () => {
        const file = fileRef.value.raw;
        let fileReader = new FileReader();
        fileReader.onload = function (e) {
          const base64 = e.target.result;
          console.log('base64', base64);
          const suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
          axios
            .post('http://localhost:9002/file/bs64Upload', {
              fileName: md5(`${file.name}_${file.size}_${file.type}`) + '.' + suffix,
              fileBs64: base64,
            })
            .then((res) => {
              console.log(res);
            });
        };
        fileReader.readAsDataURL(file);
      };
    
  • 3、后端定義一個方法,將字符串轉(zhuǎn)換為MultipartFile數(shù)據(jù)類型

    package com.course.file.utils;
    
    
    import org.springframework.web.multipart.MultipartFile;
    import sun.misc.BASE64Decoder;
    
    import java.io.*;
    
    public class Base64ToMultipartFile implements MultipartFile {
    
        private final byte[] imgContent;
        private final String header;
    
        public Base64ToMultipartFile(byte[] imgContent, String header) {
            this.imgContent = imgContent;
            this.header = header.split(";")[0];
        }
    
        @Override
        public String getName() {
            // TODO - implementation depends on your requirements
            return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1];
        }
    
        @Override
        public String getOriginalFilename() {
            // TODO - implementation depends on your requirements
            return System.currentTimeMillis() + (int) Math.random() * 10000 + "." + header.split("/")[1];
        }
    
        @Override
        public String getContentType() {
            // TODO - implementation depends on your requirements
            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);
        }
    
        public static MultipartFile base64ToMultipart(String base64) {
            try {
                String[] baseStrs = base64.split(",");
    
                BASE64Decoder decoder = new BASE64Decoder();
                byte[] b = new byte[0];
                b = decoder.decodeBuffer(baseStrs[1]);
    
                for(int i = 0; i < b.length; ++i) {
                    if (b[i] < 0) {
                        b[i] += 256;
                    }
                }
    
                return new Base64ToMultipartFile(b, baseStrs[0]);
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }
    }
    
  • 4、直接保存文件,這里就不做分片上傳

    @PostMapping("bs64Upload")
    public String bs64Upload(@RequestBody FileDTO req) throws IOException {
        // 1.將上傳的bs64轉(zhuǎn)為圖片
        String bs64 = req.getFileBs64();
        MultipartFile shard = Base64ToMultipartFile.base64ToMultipart(bs64);
        // 圖片保存到本地
        String localPath = this.FILE_PATH + req.getFileName() ;
        File dest = new File(localPath);
        shard.transferTo(dest);
        LOG.info(dest.getAbsolutePath());
        return this.FILE_DOMAIN + req.getFileName();
    }
    

六、斷點續(xù)傳

  • 1、斷點續(xù)傳主要原諒,前端在點擊上傳按鈕的時候先調(diào)用后端一個接口,判斷之前是否有上傳過記錄,如果有就返回之前上傳的分片shardIndex,前端就繼續(xù)以這個分片來上傳,如果沒有就返回0表示從0開始上傳

  • 2、后端定義一個接口根據(jù)key來查詢數(shù)據(jù)庫是否已經(jīng)有上傳過記錄

    @GetMapping("{key}")
    public Integer getShardIndexByKeyApi(@PathVariable String key) {
        LambdaQueryWrapper<FileEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FileEntity::getFileKey, key).select(FileEntity::getShardIndex);
        FileEntity fileEntity = this.fileService.getOne(queryWrapper);
        return fileEntity.getShardIndex();
    }
    
  • 3、前端在使用上傳前先調(diào)用上面的接口

     const submitUpload = () => {
        console.log('開始上傳文件', JSON.stringify(fileRef.value));
        const file = fileRef.value.raw;
        const key = md5(`${file.name}_${file.size}_${file.type}`);
        axios.get(`http://localhost:9002/file/${key}`).then((response) => {
          if (response.data > 0) {
            // 歷史上傳
            updateFile(response.data + 1);
          } else {
            // 首次上傳
            updateFile(1);
          }
        });
     };
    

七、秒傳功能

  • 1、當前端使用MD5加密后提交的名字在數(shù)據(jù)庫已經(jīng)存在,則直接拼接url返回就可以,不需要再次上傳

八、上傳到阿里OSS

  • 1、配置依賴包

    <!--    阿里云oss存儲    -->
    <dependency>
        <groupId>com.aliyun.oss</groupId>
        <artifactId>aliyun-sdk-oss</artifactId>
        <version>3.10.2</version>
    </dependency>
    
  • 2、封裝方法使用文章來源地址http://www.zghlxwxcb.cn/news/detail-742964.html

    
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.model.ObjectMetadata;
    import com.aliyun.oss.model.PutObjectResult;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URL;
    import java.util.Date;
    import java.util.List;
    import java.util.UUID;
    
    /**
     * 阿里云上傳文件
     */
    @Component
    public class OssUtil {
    
        @Value("${aliyun.oss.endpoint}")
        private String endpoint;
        @Value("${aliyun.oss.accessKeyId}")
        private String accessKeyId;
        @Value("${aliyun.oss.accessKeySecret}")
        private String accessKeySecret;
        @Value("${aliyun.oss.bucketName}")
        private String bucketName;
    
        //文件存儲目錄(自定義阿里云的)
        private String fileDir = "clouFile/";
    
        /**
         * 單個文件上傳
         *
         * @param file
         * @return
         */
        public String uploadFile(MultipartFile file) {
            // 調(diào)用封裝好的上傳文件方法
            String fileUrl = uploadImg2Oss(file);
            // 返回完整的路徑
            String str = getFileUrl(fileUrl);
            return str.trim();
        }
    
        /**
         * 單個文件上傳(指定文件名需要后綴名)
         *
         * @param file
         * @param fileName
         * @return
         */
        public String uploadFile(MultipartFile file, String fileName) {
            try {
                InputStream inputStream = file.getInputStream();
                this.uploadFile2OSS(inputStream, fileName);
                return fileName;
            } catch (Exception e) {
                return "上傳失敗";
            }
        }
    
        /**
         * 多個文件的上傳,返回路徑用,分割
         *
         * @param fileList
         * @return
         */
        public String uploadFile(List<MultipartFile> fileList) {
            String fileUrl = "";
            String str = "";
            String photoUrl = "";
            for (int i = 0; i < fileList.size(); i++) {
                fileUrl = uploadImg2Oss(fileList.get(i));
                str = getFileUrl(fileUrl);
                if (i == 0) {
                    photoUrl = str;
                } else {
                    photoUrl += "," + str;
                }
            }
            return photoUrl.trim();
        }
    
        /**
         * 獲取完整的路徑名
         *
         * @param fileUrl
         * @return
         */
        private String getFileUrl(String fileUrl) {
            if (fileUrl != null && fileUrl.length() > 0) {
                String[] split = fileUrl.split("/");
                String url = this.getUrl(this.fileDir + split[split.length - 1]);
                return url;
            }
            return null;
        }
    
        /**
         * 獲取去掉參數(shù)的完整路徑
         *
         * @param url
         * @return
         */
        private String getShortUrl(String url) {
            String[] imgUrls = url.split("\\?");
            return imgUrls[0].trim();
        }
    
        /**
         * 獲取url地址
         *
         * @param key
         * @return
         */
        private String getUrl(String key) {
            // 設(shè)置URL過期時間為20年  3600l* 1000*24*365*20
            Date expiration = new Date(new Date().getTime() + 3600l * 1000 * 24 * 365 * 20);
            // 生成URL
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
            URL url = ossClient.generatePresignedUrl(bucketName, key, expiration);
            if (url != null) {
                return getShortUrl(url.toString());
            }
            return null;
        }
    
        private String uploadImg2Oss(MultipartFile file) {
            //1、限制最大文件為20M
            if (file.getSize() > 1024 * 1024 * 20) {
                return "圖片太大";
            }
            //2、重命名文件
            String fileName = file.getOriginalFilename();
            String suffix = fileName.substring(fileName.lastIndexOf(".")).toLowerCase(); //文件后綴
            String uuid = UUID.randomUUID().toString();
            String name = uuid + suffix;
    
            try {
                InputStream inputStream = file.getInputStream();
                this.uploadFile2OSS(inputStream, name);
                return name;
            } catch (Exception e) {
                return "上傳失敗";
            }
        }
    
        /**
         * 使用阿里云上傳文件
         *
         * @param inStream 輸入流
         * @param fileName 文件名稱
         * @return
         */
        private String uploadFile2OSS(InputStream inStream, String fileName) {
            String ret = "";
            try {
                //創(chuàng)建上傳Object的Metadata
                ObjectMetadata objectMetadata = new ObjectMetadata();
                objectMetadata.setContentLength(inStream.available());
                objectMetadata.setCacheControl("no-cache");
                objectMetadata.setHeader("Pragma", "no-cache");
                objectMetadata.setContentType(getContentType(fileName.substring(fileName.lastIndexOf("."))));
                objectMetadata.setContentDisposition("inline;filename=" + fileName);
                //上傳文件
                OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
                PutObjectResult putResult = ossClient.putObject(bucketName, fileDir + fileName, inStream, objectMetadata);
                ret = putResult.getETag();
            } catch (IOException e) {
                System.out.println(e.getMessage());
            } finally {
                try {
                    if (inStream != null) {
                        inStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return ret;
        }
    
        /**
         * 獲取文件類型
         *
         * @param FilenameExtension
         * @return
         */
        private static String getContentType(String FilenameExtension) {
            if (FilenameExtension.equalsIgnoreCase(".bmp")) {
                return "image/bmp";
            }
            if (FilenameExtension.equalsIgnoreCase(".gif")) {
                return "image/gif";
            }
            if (FilenameExtension.equalsIgnoreCase(".jpeg") ||
                    FilenameExtension.equalsIgnoreCase(".jpg") ||
                    FilenameExtension.equalsIgnoreCase(".png")) {
                return "image/jpeg";
            }
            if (FilenameExtension.equalsIgnoreCase(".html")) {
                return "text/html";
            }
            if (FilenameExtension.equalsIgnoreCase(".txt")) {
                return "text/plain";
            }
            if (FilenameExtension.equalsIgnoreCase(".vsd")) {
                return "application/vnd.visio";
            }
            if (FilenameExtension.equalsIgnoreCase(".pptx") ||
                    FilenameExtension.equalsIgnoreCase(".ppt")) {
                return "application/vnd.ms-powerpoint";
            }
            if (FilenameExtension.equalsIgnoreCase(".docx") ||
                    FilenameExtension.equalsIgnoreCase(".doc")) {
                return "application/msword";
            }
            if (FilenameExtension.equalsIgnoreCase(".xml")) {
                return "text/xml";
            }
            //PDF
            if (FilenameExtension.equalsIgnoreCase(".pdf")) {
                return "application/pdf";
            }
            return "image/jpeg";
        }
    }
    

到了這里,關(guān)于spring-boot中實現(xiàn)分片上傳文件的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • spring boot 阿里云oss 文件分片上傳、斷點續(xù)傳

    spring boot 阿里云oss 文件分片上傳、斷點續(xù)傳

    文章目錄 前言 一、申請阿里云oss 二、上代碼 總結(jié) ? ? ? 阿里云對象存儲OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存儲服務(wù),可提供99.9999999999%(12個9)的數(shù)據(jù)持久性,99.995%的數(shù)據(jù)可用性。多種存儲類型供選擇,全面優(yōu)化存儲成本。 ? ? 您可以使用阿

    2024年02月07日
    瀏覽(27)
  • vue2+Spring Boot2.7 大文件分片上傳

    vue2+Spring Boot2.7 大文件分片上傳

    之前我們文章 手把手帶大家實現(xiàn) vue2+Spring Boot2.7 文件上傳功能 將了上傳文件 但如果文件很大 就不太好處理了 按正常情況甚至因為超量而報錯 這里 我弄了個足夠大的文件 我們先搭建 Spring Boot2.7 環(huán)境 首先 application.yml 代碼編寫如下 這里 我們改了他對請求大小的限制 不然

    2024年02月11日
    瀏覽(28)
  • Spring Boot整合Minio實現(xiàn)上傳憑證、分片上傳、秒傳和斷點續(xù)傳

    Spring Boot整合Minio后,前端的文件上傳有兩種方式: 文件上傳到后端,由后端保存到Minio 這種方式好處是完全由后端集中管理,可以很好的做到、身份驗證、權(quán)限控制、文件與處理等,并且可以做一些額外的業(yè)務(wù)邏輯,比如生成縮略圖、提取元數(shù)據(jù)等。 缺點也很明顯: 延遲時

    2024年02月04日
    瀏覽(24)
  • 基于Java (spring-boot)的圖書管理系統(tǒng)

    基于Java (spring-boot)的圖書管理系統(tǒng)

    一、項目介紹 該圖書管理系統(tǒng)提供了一系列功能,包括圖書管理、圖書類型管理、讀者借閱歸還圖書、用戶管理和重置密碼等。 在圖書管理功能中,管理員可以方便地進行圖書信息的管理。他們可以添加新的圖書記錄,包括書名、作者、出版社、ISBN等信息,并可以對已有的

    2024年02月04日
    瀏覽(34)
  • spring-boot 接收form表單 多文件加多字段數(shù)據(jù)(postman在form-data格式下傳數(shù)組和集合)

    spring-boot 接收form表單 多文件加多字段數(shù)據(jù)(postman在form-data格式下傳數(shù)組和集合)

    前言 該博客多用于記錄自己的問題 ?在寫項目的時候遇到這種業(yè)務(wù)情況: 需要保存整個頁面的數(shù)據(jù),數(shù)據(jù)包含 多個字段信息 和 多個文件 ?結(jié)合網(wǎng)上的處理思路,我最終實現(xiàn)了這種業(yè)務(wù)需求并整理一下 前端單獨提交字段和文件比較方便簡單,本人不太了解前端,既然前端說

    2024年02月03日
    瀏覽(20)
  • Java實現(xiàn)文件分片上傳

    Java實現(xiàn)文件分片上傳

    Java實現(xiàn)文件分片上傳 為什么要使用分片上傳 在需要上傳文件時,不可避免地會遇到上傳文件內(nèi)容過大,上傳時間太長地問題,采用文件分片上傳就可以解決這個問題。 什么是分片上傳? 簡單的說就是本來是需要一次搬一個很大的東西,比如是一大桶水,一次搬起來比較費

    2024年02月08日
    瀏覽(17)
  • JAVA面試題分享五百一十一:Spring Boot基于WebUploader實現(xiàn)超大文件上傳和斷點續(xù)傳

    JAVA面試題分享五百一十一:Spring Boot基于WebUploader實現(xiàn)超大文件上傳和斷點續(xù)傳

    目錄 前言 目標 實現(xiàn)思路 大文件分片 合并分片 斷點續(xù)傳 代碼實現(xiàn) 1、webuploader組件中,分片上傳怎么開啟? 2、webuploader組件中,文件的md5值如何計算? 3、webuploader組件中,分片文件的md5值如何計算? 4、webuploader組件中,分片上傳的的請求在哪里觸發(fā)? 5、前端、后端如何校

    2024年02月19日
    瀏覽(28)
  • Java大文件分片上傳(minio版),超詳細

    Java大文件分片上傳(minio版),超詳細

    本文使用spring boot 結(jié)合minio文件服務(wù)做的大文件分片上傳,思路:①:初始化文件調(diào)用后端接口,后端再調(diào)用minio把文件分片成幾份,生成每個分片的minio上傳url②:把提起分片好的文件依次調(diào)用上一把返回的url ③:調(diào)用合并分片文件接口,完成上傳 PS:文件并未經(jīng)過后端服務(wù)

    2024年02月07日
    瀏覽(22)
  • spring-boot定時任務(wù)

    spring-boot定時任務(wù)

    定時任務(wù)規(guī)則:0? *? *? * ? *? * 表示任意月的任意周的每天的每時的每分的0秒開始一次任務(wù)。 任務(wù)加在方法上? 開始一次任務(wù) 表示 啟動?一次方法。 0/5 *? *? 5? *? 4? 表示 每月的最后一周的第五天的任意時任意分的0秒開始 每隔5秒啟動一次任務(wù)。 定時任務(wù)表達式 還有很

    2024年01月21日
    瀏覽(32)
  • 2023 最新版IntelliJ IDEA 2023.1創(chuàng)建Java Web前(vue3)后端(spring-boot3)分離 項目詳細步驟(圖文詳解)

    2023 最新版IntelliJ IDEA 2023.1創(chuàng)建Java Web前(vue3)后端(spring-boot3)分離 項目詳細步驟(圖文詳解)

    2023 最新版IntelliJ IDEA 2023.1創(chuàng)建Java Web 項目詳細步驟(圖文詳解) 本篇使用當前Java Web開發(fā)主流的spring-boot3框架來創(chuàng)建一個Java前后端分離的項目,前端使用的也是目前前端主流的vue3進行一個簡單的項目搭建,讓你距離Java全棧開發(fā)更近一步 ?????。 使用版本: “17.0.1”

    2024年02月12日
    瀏覽(33)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包