实现文件上传及切片上传

  • 使用 Spring 框架结合阿里云 OSS 实现文件上传的代码示例
    • POM文件配置
    • 文件上传工具类
    • 控制层
      • 使用yaml配置文件(第二种用法,看公司要求)
      • 注入 OSSClient 对象及工具类(第二种用法,看公司要求)
    • 使用 Vue 前端代码实现文件上传
    • 什么是Axios
  • 使用Spring结合阿里云OSS实现大文件切片上传的代码示例
    • POM文件配置
    • 文件上传工具类
  • 大文件上传文件服务器(本地服务器Demo)
    • 前端处理
    • 前端代码
    • 后端处理
      • POM文件
    • 控制器

使用 Spring 框架结合阿里云 OSS 实现文件上传的代码示例

POM文件配置

        <dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.13.2</version></dependency>

文件上传工具类

(自己改)

public class UploadUtil {//阿里域名,自己改public static final String ALI_DOMAIN = "https://xxxx.oss-cn-guangzhou.aliyuncs.com/";public static String uploadAli(MultipartFile file) throws Exception {//生成文件名称String uuid = UUID.randomUUID().toString();String orgFileName = file.getOriginalFilename();//获取真实文件名称 xxx.jpgString ext = "." + FilenameUtils.getExtension(orgFileName);//获取拓展名字.jpgString fileName = uuid + ext;//例如:xxxx.jpgString endpoint = "http://oss-cn-guangzhou.aliyuncs.com";String accessKeyId = "xxxx";String accessKeySecret = "xxxx";// 创建OSSClient实例。OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);// 上传文件流。ossClient.putObject("xxxx-code", fileName, file.getInputStream());// 关闭OSSClient。ossClient.shutdown();return ALI_DOMAIN + fileName;}
}

控制层

@Controller
public class UploadController {@RequestMapping("/uploadImg")@ResponseBodypublic Object uploadImg(MultipartFile pic) throws Exception {//图片上传到OSSString path = UploadUtil.uploadAli(pic);return path;}@RequestMapping("/uploadImg_ck")@ResponseBodypublic Object uploadImg_ck(MultipartFile upload) throws Exception {Map<String, Object> map = new HashMap<String, Object>();String imagePath = null;if (upload != null && upload.getSize() > 0) {try {//图片保存, 返回路径imagePath = UploadUtil.uploadAli(upload);//表示保存成功map.put("uploaded", 1);map.put("url", imagePath);} catch (Exception e) {e.printStackTrace();map.put("uploaded", 0);Map<String, Object> mm = new HashMap<String, Object>();mm.put("message", e.getMessage());map.put("error", mm);}}return map;}

使用yaml配置文件(第二种用法,看公司要求)

(只是写法稍微改了点)
(自己改)

aliyun:oss:endpoint: oss-cn-shanghai.aliyuncs.comaccess-key-id: your_access_key_idaccess-key-secret: your_access_key_secretbucket-name: your_bucket_name

注入 OSSClient 对象及工具类(第二种用法,看公司要求)

@Configuration
public class AliyunConfig {@Value("${aliyun.oss.endpoint}")private String endpoint;@Value("${aliyun.oss.accessKeyId}")private String accessKeyId;@Value("${aliyun.oss.accessKeySecret}")private String accessKeySecret;@Beanpublic OSSClient ossClient() {return new OSSClient(endpoint, accessKeyId, accessKeySecret);}
}
public class UploadUtil {private final OSSClient ossClient;public static final String ALI_DOMAIN = "https://xxxx.oss-cn-guangzhou.aliyuncs.com/";public static String uploadAli(MultipartFile file) throws Exception {String uuid = UUID.randomUUID().toString();String orgFileName = file.getOriginalFilename();String ext = "." + FilenameUtils.getExtension(orgFileName);String fileName = uuid + ext;// 创建OSS实例。OSS oss = new OSSClientBuilder().build(ossClient.endpoint, ossClient.accessKeyId, ossClient.accessKeySecret);// 上传文件流。oss.putObject("xxxx-code", fileName, file.getInputStream());// 关闭OSSClient。oss.shutdown();return ALI_DOMAIN + fileName;}
}

使用 Vue 前端代码实现文件上传

<template><div><form><input type="file" ref="fileInput" @change="uploadFile"/></form></div>
</template>
<script>
import axios from 'axios';export default {methods: {async uploadFile() {const file = this.$refs.fileInput.files[0];const formData = new FormData();formData.append('file', file);try {const response = await axios.post('/api/upload', formData);console.log(response.data);} catch (error) {console.error(error);}}}
};
</script>

在上面的代码中,我们使用了 Vue 的模板语法创建了一个文件选择控件。在选择文件时,我们会调用 uploadFile 方法。

在 uploadFile 方法中,我们使用了 Axios 来发送请求到后端,并使用 FormData 对象封装文件。请求成功后,我们会在控制台中输出服务器返回的结果。

什么是Axios

Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 node.js。它可以用来发送 HTTP 请求,例如 GET、POST、PUT 和 DELETE 等。Axios 的 API 设计简单易用,而且支持同步和异步请求。

与传统的 jQuery AJAX 方法不同,Axios 更加现代化,并且它使用了 Promise 语法,因此请求代码更加简洁易读。此外,Axios 还支持拦截器、自动转换 JSON 响应、自动发送 cookie 等功能。

如果需要在 Vue 项目中发送 HTTP 请求,Axios 是一个非常好的选择。

使用Spring结合阿里云OSS实现大文件切片上传的代码示例

POM文件配置

        <dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.13.2</version></dependency>

文件上传工具类

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.*;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;/*** 使用阿里云OSS上传文件*/
public class OSSUploader {public static final String ALI_DOMAIN = "https://xxxx-code.oss-cn-guangzhou.aliyuncs.com/";private static final String endpoint = "oss-cn-hangzhou.aliyuncs.com";private static final String accessKeyId = "your_accessKeyId";private static final String accessKeySecret = "your_accessKeySecret";private static final String bucketName = "your_bucketName";/*** 上传文件到阿里云OSS** @param file       上传的文件* @return String    文件于阿里云OSS的保存路径*/public static String uploadFile(MultipartFile file) throws Exception {if (file == null || file.isEmpty()){return "";}String uuid = UUID.randomUUID().toString();String orgFileName = file.getOriginalFilename();//获取真实文件名称 xxx.jpgString ext = "." + FilenameUtils.getExtension(orgFileName);//获取拓展名字.jpgString fileName = uuid + ext;//xxxx.jpgOSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);try {// 创建InitiateMultipartUploadRequest对象。InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, fileName);// 初始化分片。InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);// 返回uploadId,它是分片上传事件的唯一标识。您可以根据该uploadId发起相关的操作,例如取消分片上传、查询分片上传等。String uploadId = upresult.getUploadId();// partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。List<PartETag> partETags = new ArrayList<PartETag>();// 每个分片的大小,用于计算文件有多少个分片。单位为字节。final long partSize = 1 * 1024 * 1024L;   //1 MB。long fileLength = file.getSize();int partCount = (int) (fileLength / partSize);if (fileLength % partSize != 0) {partCount++;}// 遍历分片上传。for (int i = 0; i < partCount; i++) {long startPos = i * partSize;long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;InputStream instream = file.getInputStream();// 跳过已经上传的分片。instream.skip(startPos);UploadPartRequest uploadPartRequest = new UploadPartRequest();uploadPartRequest.setBucketName(bucketName);uploadPartRequest.setKey(fileName);uploadPartRequest.setUploadId(uploadId);uploadPartRequest.setInputStream(instream);// 设置分片大小。除了最后一个分片没有大小限制,其他的分片最小为100 KB。uploadPartRequest.setPartSize(curPartSize);// 设置分片号。每一个上传的分片都有一个分片号,取值范围是1~10000,如果超出此范围,OSS将返回InvalidArgument错误码。uploadPartRequest.setPartNumber(i + 1);// 每个分片不需要按顺序上传,甚至可以在不同客户端上传,OSS会按照分片号排序组成完整的文件。UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);// 每次上传分片之后,OSS的返回结果包含PartETag。PartETag将被保存在partETags中。partETags.add(uploadPartResult.getPartETag());}CompleteMultipartUploadRequest completeMultipartUploadRequest =new CompleteMultipartUploadRequest(bucketName, fileName, uploadId, partETags);// 完成分片上传。CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest);System.out.println(completeMultipartUploadResult.getETag());} catch (OSSException oe) {System.out.println("Caught an OSSException, which means your request made it to OSS, "+ "but was rejected with an error response for some reason.");System.out.println("Error Message:" + oe.getErrorMessage());System.out.println("Error Code:" + oe.getErrorCode());System.out.println("Request ID:" + oe.getRequestId());System.out.println("Host ID:" + oe.getHostId());} catch (ClientException ce) {System.out.println("Caught an ClientException, which means the client encountered "+ "a serious internal problem while trying to communicate with OSS, "+ "such as not being able to access the network.");System.out.println("Error Message:" + ce.getMessage());} finally {if (ossClient != null) {ossClient.shutdown();}}return ALI_DOMAIN + fileName;}
}

大文件上传文件服务器(本地服务器Demo)

转载来自程序员小潘,稍微做了些改动(部分报错已改,持续更新)
之前的图片、Excel等上传做的很简单,直接表单提交后端用MultipartFile接收保存到磁盘就行了。
但是针对大文件的上传,需要做额外的处理,否则可能会遇到如下问题:

  1. 文件过大,超出服务端的请求大小限制(如SpringMVC,默认文件上传最大1MB)。
  2. 请求的时间过长,请求超时。
  3. 客户端网络不好的话,容易传输中断,必须整个文件重传。

前端处理

大文件分片上传,是需要前端和后端配合操作的。
整体流程是:前端将大文件进行分片,例如一个50MB的文件,分成10片,每个片5MB。然后发10个HTTP请求,将这10个分片数据发送给后端,后端根据分片的下标和Size来往磁盘文件的不同位置写入分片数据,10个分片全部写完后即得到一个完整的文件。

前端代码

需要自己下一个jquery.min.js,她这个链接(http://libs.baidu.com/jquery/2.0.0/jquery.min.js)访问不了

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script><script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.js"></script>
</head>
<body>
<input type="file" name="file" id="file">
<button id="upload" onClick="upload()">upload</button>
</body>
</html><script>const sliceSize = 5 * 1024 * 1024; // 每个文件切片大小定为5MB//发送请求function upload() {const blob = document.getElementById("file").files[0];const fileSize = blob.size;// 文件大小const fileName = blob.name;// 文件名//计算文件切片总数const totalSlice = Math.ceil(fileSize / sliceSize);// 循环上传for (let i = 1; i <= totalSlice; i++) {let chunk;if (i == totalSlice) {// 最后一片chunk = blob.slice((i - 1) * sliceSize, fileSize - 1);//切割文件} else {chunk = blob.slice((i - 1) * sliceSize, i * sliceSize);}const formData = new FormData();formData.append("file", chunk);formData.append("md5", md5(blob));formData.append("name", fileName);formData.append("size", fileSize);formData.append("chunks", totalSlice);formData.append("chunk", i);$.ajax({url: 'http://localhost:8080/chunk/upload',type: 'POST',cache: false,data: formData,processData: false,contentType: false,async: false});}}
</script>

后端处理

后端接收到分片数据后,要根据分片的下标和分片的大小来往文件的指定位置写入分片数据。
例如:分片大小为1MB,第一个分片就要往文件的第0个字节开始,写入1048576字节的数据。第二个分片就要往文件的第1048576个字节开始,写入1048576字节的数据,以此类推。待所有的分片数据全部写入完成后,即得到一个完整的文件。

后端处理流程如下:

RandomAccessFile
分片数据的写入,需要对文件进行定位,移动访问指针。
JDK提供了java.io.RandomAccessFile类,支持对文件进行随机的读写操作。
在Linux平台上,所有打开的文件都有一个文件描述符(FD),文件描述符自身维护了一个文件偏移量(current file offset),简称cfo,通过lseek函数可以移动文件的读写指针,RandomAccessFile的seek()方法就是调用了Linux的lseek系统函数来实现的。

通过RandomAccessFile.seek()移动访问指针,然后写入分片数据。

后端处理代码如下:

POM文件

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.8.0</version></dependency>

控制器

import org.apache.commons.io.FilenameUtils;
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;
import java.io.RandomAccessFile;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;@RestController
public class FileController {// 存放文件的临时目录private static final String DATA_DIR = System.getProperty("user.dir") + "/temp/";// 文件MD5的缓存容器private static final ConcurrentMap<String, File> MD5_CACHE = new ConcurrentHashMap<>();/*** 大文件分片上传** @param name          文件名* @param md5           文件MD5值* @param size          文件大小* @param chunks        总的分片数* @param chunk         当前分片数* @param multipartFile 分片流* @throws IOException*/@PostMapping("/chunk/upload")public void chunkUpload(String name,String md5,Long size,Integer chunks,Integer chunk,@RequestParam("file") MultipartFile multipartFile) throws IOException {// 是否生成了文件File targetFile = MD5_CACHE.get(md5);if (targetFile == null) {// 没有生成的话就生成一个新的文件,没有做并发控制,多线程上传会出问题targetFile = new File(DATA_DIR, UUID.randomUUID().toString() + "." + FilenameUtils.getExtension(name));targetFile.getParentFile().mkdirs();MD5_CACHE.put(md5, targetFile);}// 可以对文件的任意位置进行读写RandomAccessFile accessFile = new RandomAccessFile(targetFile, "rw");boolean finished = chunk == chunks;//是否最后一片if (finished) {// 移动指针到指定位置accessFile.seek(size - multipartFile.getSize());} else {accessFile.seek((chunk - 1) * multipartFile.getSize());}// 写入分片的数据accessFile.write(multipartFile.getBytes());accessFile.close();if (finished) {System.out.println("success.");// 上传成功MD5_CACHE.remove(md5);System.out.println(targetFile);}}
}

文件分片上传的所有请求中,必须有一个唯一标识可以将所有的分片数据关联起来,她这里用的是文件的MD5值。
存在问题:如果上传的是图片,存在图片格式问题,下载到本地的图片打开后无法显示。如果有解决办法,请评论区留言,感激不尽!
若有其它bug请反馈

如有错误,还请多多指教!
转载或者引用本文内容请注明来源及原作者:橘足轻重;

【Spring】使用 Spring 框架实现文件上传及切片上传的代码示例,非常长,建议挑选适合的看相关推荐

  1. 文件上传、切片上传、秒传等

    切片上传与秒传方案 总览 理解的方案对比 一次性文件上传 切片上传 额外话题 总览 浏览器情况下: 针对大文件上传,一次性传输文件会出现网络中断等情况.不会传输成功. 把大文件切分成多个二进制流格式切 ...

  2. 前端大文件上传及切片上传-提升上传效率

    一.使用场景: 1.大文件上传 2.网络环境环境不好,存在需要重传风险的场景 二.名词解释: 切片上传:也叫分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块来进行分别上传,上 ...

  3. Spring源码深度解析,Spring源码以及Bean的生命周期(五)(附代码示例:)

    五)Bean 的生命周期,创建---初始化---销毁的过程 目录 五)Bean 的生命周期,创建---初始化---销毁的过程 一 ,  指定初始化方法 init-method 方法​ 二 ,指定销毁 ...

  4. C#大文件上传支持切片上传

    前言:因自己负责的项目(jetty内嵌启动的SpringMvc)中需要实现文件上传,而自己对java文件上传这一块未接触过,且对 Http 协议较模糊,故这次采用渐进的方式来学习文件上传的原理与实践. ...

  5. 如何给单据分录上增加核算项目的F7(代码示例)。

    用BIM在单据上先增加核算项目的列,注意如果BIM中选择不到核算项目的业务单元,还需要先手工在BIM中先引入一个,然后发布,在实现类中增加如下代码(须根据自己的实际修改控件名和自定义核算项目类别编码) ...

  6. vue 文件及描述信息一起上传_用Vue实现一个大文件上传和断点续传

    前言 这段时间面试官都挺忙的,频频出现在博客文章标题,虽然我不是特别想蹭热度,但是实在想不到好的标题了-.-,蹭蹭就蹭蹭 :) 事实上我在面试的时候确实被问到了这个问题,而且是一道在线 coding ...

  7. 【Groovy】MOP 元对象协议与元编程 ( 使用 Groovy 元编程进行函数拦截 | 使用 MetaClass 进行方法拦截 | 对象上拦截方法 | 类上拦截方法 )

    文章目录 一.使用 MetaClass 进行方法拦截 1.使用 MetaClass 在单个对象上进行方法拦截 2.使用 MetaClass 在类上进行方法拦截 二.完整代码示例 1.对象方法拦截 2. ...

  8. 11月30在spring mvc中使用Validator框架和文件上传

    首先回顾了spring mvc中的表单验证和业务逻辑校验失败后,回到表单页面中显示错误信息的整个内部运行流程. 表单校验出错后回到表单注册页面是由默认的SimpleFormController的pro ...

  9. Spring MVC更多家族成员----文件上传---06

    Spring MVC更多家族成员----文件上传---06 本节导读 文件上传与MultipartResolver 使用MultipartResolver进行文件上传的简单分析 StandardSer ...

最新文章

  1. 程序员必读10本算法书推荐
  2. Oops 的栈信息分析
  3. 局内网用户访问wamp本地站点
  4. domdocument php 扩展_php使用自带dom扩展进行元素匹配的原理解析
  5. 预安装CentOS6.5,虚拟机VMware Workstation12配置
  6. C# Excel导入、导出
  7. ASP.NET之MVC 微信公众号授权给第三方平台的技术实现流程一(获取第三方平台access_token)...
  8. Json类型的转化 及 JsonArray,JsonObject详解
  9. 单片微型计算机原理和应用答案,《单片微机原理及应用》 试卷A及参考答案
  10. DES加密解密-java
  11. WannaCry 勒索软件
  12. JavaScript---网络编程(8)-DHTML技术演示(1)
  13. 如何使用Python的第三方库you-get下载视频
  14. SAP中标准价和移动平均价的学习笔记
  15. 最简单的验证码(利用JSP生成验证码)
  16. 项目中加入百度联盟广告。
  17. 教师计算机基础知识培训简报,信息技术能力提升培训简报.doc
  18. Leads、SDR、MQL、SQL、L2C术语概念,线索创建与清洗、线索分级模型、线索流转策略、线索的统计与分析
  19. nvm切换node nvm use 17.2.0 exit status 5: �ܾ����ʡ� exit status 1: ���ļ��Ѵ���ʱ���޷��������ļ���
  20. Java集合框架(List)

热门文章

  1. 车载诊断协议UDS——DTC 判定机制以及DTC Status个人见解
  2. Assetbundle打包及加载入门详解(二)
  3. Google Talk可以使用了(我的试用手记)
  4. 阿里财报今天公布!新势力将成为阿里巴巴集团的主营收
  5. linux ifconfig ens33没有显示ip地址解决办法
  6. 拿来即用的java实现的微信分享教程(全步骤)
  7. rake db:migrate db:reset和db:schema:load之间的区别
  8. 切勿临渴再掘井,还需未雨先绸缪
  9. unity中使用htc vive
  10. 玩转 Jasper Report(3)Spring Boot 2.4 集成 Jasper Report 导出 PDF