前言:
现时兄弟们对“java上传文件的进度条”大约比较讲究,各位老铁们都想要了解一些“java上传文件的进度条”的相关资讯。那么小编同时在网上搜集了一些关于“java上传文件的进度条””的相关知识,希望我们能喜欢,你们一起来了解一下吧!1. 前言
简要地记录下 SpringBoot 与 Vue 实现文件的上传与下载
2. 简单案例2.1 功能需求
前台使用 ElementUI 的 Upload 组件或者是 Axios,后台使用 SpringBoot 来实现文件的上传与下载
2.2 开发环境IDEA-2019.1SpringBoot-2.0.2.RELEASEMaven-3.5.3HBuilderX2.3 编写代码2.3.1 上传、下载2.3.1.1 前端
使用 ElementUI 的 Upload 组件
我这里在 html 页面引入:
<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <title></title> <script src="./js/vue.min.js"></script> <link rel="stylesheet" href="./css/index.css"> <script src="./js/index.js"></script> </head> <body> <div id="app"> <div style="top:100px;width:300px"> <el-form ref="upload" :model="form" label-width="120px"> <el-form-item label="请输入文件名" required> <el-input v-model="form.fileName" auto-complete="off" class="el-col-width"></el-input> </el-form-item> <el-form-item> <el-button size="small" type="primary" @click="handleDownLoad">下载</el-button> </el-form-item> <el-form-item> <el-upload class="upload-demo" :action="uploadUrl" :before-upload="handleBeforeUpload" :on-error="handleUploadError" multiple :limit="5" :on-exceed="handleExceed" :file-list="fileList" :on-success="onSuccess"> <el-button size="small" type="primary">点击上传</el-button> <div slot="tip" class="el-upload__tip">不超过10Kb</div> </el-upload> </el-form-item> </el-form> </div> </div> </body> <script type="application/javascript"> var app = new Vue({ el: '#app', data: { form: { fileName: 'test.txt' }, // 后台请求url uploadUrl: ';, fileList: [], isUpload: false }, methods: { handleExceed(files, fileList) { this.$message.warning(`当前限制选择 5 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`); }, handleUploadError(error, file) { this.$notify.error({ title: 'error', message: '上传出错:' + error, type: 'error', position: 'bottom-right' }) }, handleBeforeUpload(file) { this.isUpload = file.size / (1024 * 10) <= 1 ? '1' : '0' if (this.isUpload === '0') { this.$message({ message: '上传文件大小不能超过10k', type: 'error' }) } return this.isUpload === '1' ? true : false }, onSuccess() { this.$message.success(`上传成功!`) }, handleDownLoad() { window.location.href = `` + this.form.fileName } } }) </script></html>uploadUrl:data 中的属性。指的是上传到后台的地址主要是 el-upload 标签,当点击“点击上传”按钮时,选择文件后,会自动提交(auto-upload)到后台,auto-upload 属性默认为 true,可修改。
前台页面:
上传:可以上传多个文件,最多5个,但每次只能上传一个文件,需要上传多次。然后,文件大小不能超过 10 Kb(handleBeforeUpload()方法中有校验)。否则,上传失败。
下载:前端的下载就是通过链接访问后台地址即可。这里的 fileName 是作为一个测试的下载文件,你可以换成其他的文件,只要本地磁盘存在这个文件就行(重点看后台代码逻辑,在下面呢)。
2.3.1.2 后端
后台是一个父子关系的多模块项目。不太熟悉的话,可以参考此博文:
Maven 多模块项目的创建与配置
项目结构图
父 POM 文件
<?xml version="1.0" encoding="UTF-8"?>... <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> </parent> <properties> <!-- 在properties中统一控制依赖包的版本,更清晰--> <lombok.version>1.18.8</lombok.version> <junit.test.version>4.11</junit.test.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.test.version}</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement></project>
sb_vue(此项目) POM 文件
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--父模块pom中使用dependencyManagement来管理依赖版本号,子模块pom中不需要再写版本号--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency></dependencies>
application.yml
spring: servlet: multipart: enabled: true max-file-size: 10MB # 单个文件上传的最大上限 max-request-size: 10MB # 一次请求总大小上限
Controller 层
@RestController@RequestMapping("/file")@CrossOrigin // 跨域public class FileController { @Autowired private FileService fileService; // 文件上传 @RequestMapping("/upload") public ResultVo<String> uploadFile(@RequestParam("file") MultipartFile file) { return fileService.uploadFile(file); } // 文件下载 @RequestMapping("/download") public ResultVo<String> downloadFile(@RequestParam("fileName") String fileName, final HttpServletResponse response) { return fileService.downloadFile(fileName, response); }}
Service 层
这里只贴出实现类的代码了
@Slf4j // 日志注解@Servicepublic class FileServiceImpl implements FileService { @Override public ResultVo<String> uploadFile(MultipartFile file) { if (file.isEmpty()) { log.error("上传的文件为空"); throw new ParameterValidateExcepiton(ResultCodeEnum.PARAMETER_ERROR.getCode(), "上传的文件为空"); } try { FileUtil.uploadFile(file); } catch (IOException e) { log.error("文件{}上传失败", file); return ResultUtil.error("上传失败"); } log.info("文件上传成功"); return ResultUtil.success("上传成功!"); } @Override public ResultVo<String> downloadFile(String fileName, HttpServletResponse response) { if (fileName.isEmpty()) { log.error("文件名为空"); throw new ParameterValidateExcepiton(ResultCodeEnum.PARAMETER_ERROR.getCode(), "文件名为空"); } return FileUtil.downloadFile(fileName, response); }}
FileUtil
文件工具类
@Slf4jpublic class FileUtil { // 文件上传路径 private static final String FILE_UPLOAD_PATH = "upload" + File.separator; // 文件下载路径 private static final String FILE_DOWNLOAD_PATH = "download" + File.separator; // 日期路径 private static final String DATE_PATH = DateUtil.getNowStr() + File.separator; // 根路径 private static final String ROOT_PATH = "E:" + File.separator; // 下划线 private static final String UNDER_LINE = "_"; // 默认字符集 private static final String DEFAULT_CHARSET = "utf-8"; // 上传文件 public static String uploadFile(MultipartFile file) throws IOException{ // 获取上传的文件名称(包含后缀名) String oldFileName = file.getOriginalFilename(); // 获取文件后缀名,将小数点“.” 进行转译 String[] split = oldFileName.split("\\."); // 文件名 String fileName = null; StringBuilder builder = new StringBuilder(); if (split.length > 0) { String suffix = split[split.length - 1]; for (int i = 0; i < split.length -1; i++) { builder.append(split[i]).append(UNDER_LINE); } // 防止文件名重复 fileName = builder.append(System.nanoTime()).append(".").append(suffix).toString(); } else { fileName = builder.append(oldFileName).append(UNDER_LINE).append(System.nanoTime()).toString(); } // 上传文件的存储路径 String filePath = ROOT_PATH + FILE_UPLOAD_PATH + DATE_PATH; // 生成文件夹 mkdirs(filePath); // 文件全路径 String fileFullPath = filePath + fileName; log.info("上传的文件:" + file.getName() + "," + file.getContentType() + ",保存的路径为:" + fileFullPath); // 转存文件 Streams.copy(file.getInputStream(), new FileOutputStream(fileFullPath), true); //file.transferTo(new File(fileFullPath)); //Path path = Paths.get(fileFullPath); //Files.write(path,file.getBytes()); return fileFullPath; } // 根据文件名下载文件 public static ResultVo<String> downloadFile(String fileName, HttpServletResponse response) { InputStream in = null; OutputStream out = null; try { // 获取输出流 out = response.getOutputStream(); setResponse(fileName, response); String downloadPath = new StringBuilder().append(ROOT_PATH).append(FILE_DOWNLOAD_PATH).append(fileName).toString(); File file = new File(downloadPath); if (!file.exists()) { log.error("下载附件失败,请检查文件" + downloadPath + "是否存在"); return ResultUtil.error("下载附件失败,请检查文件" + downloadPath + "是否存在"); } // 获取输入流 in = new FileInputStream(file); if (null == in) { log.error("下载附件失败,请检查文件" + fileName + "是否存在"); throw new FileNotFoundException("下载附件失败,请检查文件" + fileName + "是否存在"); } // 复制 IOUtils.copy(in, response.getOutputStream()); response.getOutputStream().flush(); try { close(in, out); } catch (IOException e) { log.error("关闭流失败"); return ResultUtil.error(ResultCodeEnum.CLOSE_FAILD.getCode(), "关闭流失败"); } } catch (IOException e) { log.error("响应对象response获取输出流错误"); return ResultUtil.error("响应对象response获取输出流错误"); } return ResultUtil.success("文件下载成功"); } // 设置响应头 public static void setResponse(String fileName, HttpServletResponse response) { // 清空输出流 response.reset(); response.setContentType("application/x-download;charset=GBK"); try { response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes(DEFAULT_CHARSET), "iso-8859-1")); } catch (UnsupportedEncodingException e) { log.error("文件名{}不支持转换为字符集{}", fileName, DEFAULT_CHARSET); } } // 关闭流 public static void close(InputStream in, OutputStream out) throws IOException{ if (null != in) { in.close(); } if (null != out) { out.close(); } } // 根据目录路径生成文件夹 public static void mkdirs(String path) { File file = new File(path); if(!file.exists() || !file.isDirectory()) { file.mkdirs(); } }}
DateUtil
日期工具类
public class DateUtil { // 默认日期字符串格式 "yyyy-MM-dd" public final static String DATE_DEFAULT = "yyyy-MM-dd"; // 日期字符串格式 "yyyyMMdd" public final static String DATE_YYYYMMDD = "yyyyMMdd"; // 格式 map private static Map<String, SimpleDateFormat> formatMap; // 通过格式获取 SimpleDateFormat 对象 private static SimpleDateFormat getFormat(String pattern) { if (formatMap == null) { formatMap = new HashMap<>(); } SimpleDateFormat format = formatMap.get(pattern); if (format == null) { format = new SimpleDateFormat(pattern); formatMap.put(pattern, format); } return format; } // 将当前时间转换为字符串 public static String getNowStr() { return LocalDate.now().format(DateTimeFormatter.ofPattern(DATE_YYYYMMDD)); }}
ResultVo
统一接口的返回值
@Datapublic class ResultVo<T> { // 错误码. private Integer code; // 提示信息. private String msg; // 具体的内容. private T data;}
ResultUtil
返回界面的工具类
public class ResultUtil { public static ResultVo success() { return success(null); } public static ResultVo success(Object object) { ResultVo result = new ResultVo(); result.setCode(ResultCodeEnum.SUCCESS.getCode()); result.setMsg("成功"); result.setData(object); return result; } public static ResultVo success(Integer code, Object object) { return success(code, null, object); } public static ResultVo success(Integer code, String msg, Object object) { ResultVo result = new ResultVo(); result.setCode(code); result.setMsg(msg); result.setData(object); return result; } public static ResultVo error( String msg) { ResultVo result = new ResultVo(); result.setCode(ResultCodeEnum.ERROR.getCode()); result.setMsg(msg); return result; } public static ResultVo error(Integer code, String msg) { ResultVo result = new ResultVo(); result.setCode(code); result.setMsg(msg); return result; }}
ParameterValidateExcepiton
自定义异常
@Getterpublic class ParameterValidateExcepiton extends RuntimeException { // 错误码 private Integer code; // 错误消息 private String msg; public ParameterValidateExcepiton() { this(ResultCodeEnum.PARAMETER_ERROR.getCode(), ResultCodeEnum.PARAMETER_ERROR.getMessage()); } public ParameterValidateExcepiton(String msg) { this(ResultCodeEnum.PARAMETER_ERROR.getCode(), msg); } public ParameterValidateExcepiton(Integer code, String msg) { super(msg); this.code = code; this.msg = msg; }}
ExceptionControllerAdvice
统一异常处理类
@RestControllerAdvicepublic class ExceptionControllerAdvice { // 处理文件为空的异常 @ExceptionHandler(ParameterValidateExcepiton.class) public ResultVo<String> fileExceptionHandler(ParameterValidateExcepiton excepiton) { return ResultUtil.error(ResultCodeEnum.PARAMETER_ERROR.getCode(), excepiton.getMsg()); } // 文件不存在异常 @ExceptionHandler(FileNotFoundException.class) public ResultVo<String> fileNotFoundExceptionHandler(FileNotFoundException exception) { return ResultUtil.error(ResultCodeEnum.FILE_NOT_EXIST.getCode(), exception.getMessage()); }}
ResultCodeEnum
统一响应码
@Getterpublic enum ResultCodeEnum { SUCCESS(200, "成功") , ERROR(301, "错误") , UNKNOWERROR(302, "未知错误") , PARAMETER_ERROR(303, "参数错误") , FILE_NOT_EXIST(304, "文件不存在") , CLOSE_FAILD(305, "关闭流失败") ; private Integer code; private String message; ResultCodeEnum(Integer code, String message) { this.code = code; this.message = message; }}
总体上看,代码量有点大哈(主要是代码写得比较优雅),各位就将就点吧。本来是想着上传到 github 上面,但公司电脑用的是内网,无法访问到外网。即使配置了代理,但 IDEA 也无法连接到 github 上面。但又不想残忍地只贴出部分代码(以免部分读者很迷惑),所以,这里就全贴出来了哈。
3. 使用 Axios
看看上面的前端上传代码,使用 Upload 组件自动地上传到后台,无法接收后台接口传过来的值。这就会导致一个问题:如果上传过程中,遇见什么错误,导致上传失败,那么就需要提示给用户了。但这种做法是无法实现的。看了多篇博客,大多是使用了 http-request() 方法。在这个方法里,通过 axios 请求后台,并能获取后台的返回值。
3.1 前台
前台代码
<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <title></title> <script src="./js/vue.min.js"></script> <!-- 引入样式 --> <link rel="stylesheet" href="./css/index.css"> <!-- 引入组件库 --> <script src="./js/index.js"></script> <!-- 引入axios --> <script src="js/axios.min.js"></script> <style> .app { margin-left: 200px; margin-top: 200px; } </style> </head> <body> <div id="app" class="app"> <el-upload class="upload-demo" ref="upload" action="" :http-request="submitUpload" :before-upload="beaforeUpload" :limit="1" :on-exceed="onExceed" :auto-upload="true"> <el-button slot="trigger" size="small" type="primary">选取文件</el-button> <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div> </el-upload> </div> </body> <script type="application/javascript"> var app = new Vue({ el: '#app', data: { }, methods: { onExceed(files, fileList) { this.$message.warning(`当前限制选择 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`); }, // 上传文件 submitUpload(content) { console.log(content) let file = content.file; if (file != null && file != '') { let isJPG = file.type === 'image/jpeg' let isPNG = file.type === 'image/png' let isLt2M = file.size / 1024 / 1024 < 0.5 if (!isPNG && !isJPG) { this.$message.error('上传图片只能是 JPG/PNG 格式!') return false } else if (!isLt2M) { this.$message.error('上传图片大小不能超过 200kb!') return false } else if (isLt2M && (isPNG || isJPG)) { let data = new FormData(); data.append("file", file) //console.log(data.get('file')) let url = '; let headers = { 'Content-Type': 'multipart/form-data' } /* axios({ method: 'post', url: ';, data: data, headers: { 'Content-Type': 'multipart/form-data' } */ axios.post(url, data, headers) .then(res => { if (res.data.code === 200) { this.$message({ type: 'success', message: res.data.msg }) } else { this.$message({ type: 'warning', message: res.data.msg }) } }).catch(error => { this.$message({ type: 'error', message: error }) }) } } } } }) </script></html>
在 submitUpload() 方法中,先对上传的文件进行校验,只有校验通过的文件才能去请求后台。
3.2 后台代码
后台编码不变
前后端项目启动,发现依旧能交互哈。
总结
以上就是今天要讲的内容,本文仅仅简单地介绍了使用 SpringBoot 和 Vue 实现文件的上传与下载。主要考虑到公司中有使用到 Vue 中的 Upload 组件上传文件,所以,自己也就接触了下 它。奈何自己对 Vue 的造诣不深,使用 axios 进行文件上传的方法也就找到了那一个,但我总感觉不是很理想,如果,有读者有更好的想法可以分享一下。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:
标签: #java上传文件的进度条 #netinputfile上传文件 #如何用axios发送数据给后端 #vue3中文文档下载 #vue实现文件上传到服务器