听说微信搜索《Java鱼仔》会变更强哦!
本文收录于JavaStarter ,里面有我完整的Java系列文章,学习或面试都可以看看哦
(一)概述
文件上传下载一直都是一个系统最常用也是最基本的功能点,刚好最近公司的项目上有用到这个功能,于是自己就用SpringBoot也写了一个简化的版本,已实现文件的上传和下载功能。
(二)创建项目
首先创建一个SpringBoot的项目,接着引入相关的依赖,因为涉及到数据库的操作,所以依赖会比较多一些。
2.1 依赖引入
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.9</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
2.2 接口通用返回类编写
编写一个接口的通用返回体,这个在之前的博客中我专门写过,现在就直接拿来用了:
publicenumResponseCode { // 系统模块SUCCESS(0, "操作成功"), ERROR(1, "操作失败"), SERVER_ERROR(500, "服务器异常"), // 通用模块 1xxxxILLEGAL_ARGUMENT(10000, "参数不合法"), REPETITIVE_OPERATION(10001, "请勿重复操作"), ACCESS_LIMIT(10002, "请求太频繁, 请稍后再试"), MAIL_SEND_SUCCESS(10003, "邮件发送成功"), // 用户模块 2xxxxNEED_LOGIN(20001, "登录失效"), USERNAME_OR_PASSWORD_EMPTY(20002, "用户名或密码不能为空"), USERNAME_OR_PASSWORD_WRONG(20003, "用户名或密码错误"), USER_NOT_EXISTS(20004, "用户不存在"), WRONG_PASSWORD(20005, "密码错误"), // 文件模块 3xxxxFILE_EMPTY(30001,"文件不能空"), FILE_NAME_EMPTY(30002,"文件名称不能为空"), FILE_MAX_SIZE(30003,"文件大小超出"), ; ResponseCode(Integercode, Stringmsg) { this.code=code; this.msg=msg; } privateIntegercode; privateStringmsg; publicIntegergetCode() { returncode; } publicvoidsetCode(Integercode) { this.code=code; } publicStringgetMsg() { returnmsg; } publicvoidsetMsg(Stringmsg) { this.msg=msg; } }
返回体:
publicclassResult { privateintcode; privateStringmessage; privateObjectdata; }
2.3 配置一下解决跨域问题的配置类
在SpringBoot中有多种解决跨域的方法,这里选择其中一种
publicclassWebMvcConfigimplementsWebMvcConfigurer { publicvoidaddCorsMappings(CorsRegistryregistry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE") .maxAge(3600) .allowCredentials(true); } }
到这里为止,基本的项目配置就算结束了,接下来就是功能的实现了。
(三)实现文件上传下载
3.1 创建表
首先创建一张表来记录文件的路径、名称、后缀等信息:
CREATETABLE`file` ( `id`int(11) NOTNULLAUTO_INCREMENT, `filePath`varchar(255) DEFAULTNULL, `fileName`varchar(255) DEFAULTNULL, `fileSuffix`varchar(255) DEFAULTNULL, PRIMARYKEY (`id`) ) ENGINE=InnoDBAUTO_INCREMENT=4DEFAULTCHARSET=utf8;
3.2 编写实体类
写一个文件对象,和数据库中的字段相对应:
publicclassFilesimplementsSerializable { privatestaticfinallongserialVersionUID=1L; /*** 文件存储路径*/privateStringfilePath; /*** 文件名称*/privateStringfileName; /*** 文件后缀名*/privateStringfileSuffix; }
3.3 配置application.properties
在配置文件中将服务端口,数据库连接方式以及文件的保存路径配置一下:
server.port=8080spring.datasource.url=jdbc:mysql://localhost:3306/test7?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8spring.datasource.username=rootspring.datasource.password=123456spring.datasource.driver-class-name=com.mysql.cj.jdbc.Drivermybatis.mapper-locations=classpath:mapper/*.xmlfile.save-path=E:/temp/files
3.4 编写Controller
新建一个类叫FileController,用来写接口,文件上传下载接口的代码已经给了注释,Spring中提供了一个MultipartFile类,用来接收前台传过来的文件,这里直接使用即可。
"/api") (publicclassFileController { privateFileServicefileService; value="/upload",method=RequestMethod.POST) (publicResultupLoadFiles(MultipartFilemultipartFile){ if (multipartFile.isEmpty()){ returnnewResult(ResponseCode.FILE_EMPTY.getCode(),ResponseCode.FILE_EMPTY.getMsg(),null); } returnfileService.upLoadFiles(multipartFile); } value="/download/{id}",method=RequestMethod.GET) (publicvoiddownloadFiles( ("id") Stringid, HttpServletRequestrequest, HttpServletResponseresponse){ OutputStreamoutputStream=null; InputStreaminputStream=null; BufferedInputStreambufferedInputStream=null; byte[] bytes=newbyte[1024]; Filesfiles=fileService.getFileById(id); StringfileName=files.getFileName(); // 获取输出流try { response.setHeader("Content-Disposition", "attachment;filename="+newString(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1)); response.setContentType("application/force-download"); inputStream=fileService.getFileInputStream(files); bufferedInputStream=newBufferedInputStream(inputStream); outputStream=response.getOutputStream(); inti=bufferedInputStream.read(bytes); while (i!=-1){ outputStream.write(bytes,0,i); i=bufferedInputStream.read(bytes); } } catch (IOExceptione) { e.printStackTrace(); }finally { try { if (inputStream!=null){ inputStream.close(); } if (outputStream!=null){ outputStream.close(); } if (bufferedInputStream!=null){ bufferedInputStream.close(); } } catch (IOExceptione) { e.printStackTrace(); } } } }
所有的业务都写在service中,因此需要有fileService接口以及fileServiceImpl实现类:
publicinterfaceFileService { /*** 文件上传接口* @param file* @return*/ResultupLoadFiles(MultipartFilefile); /*** 根据id获取文件* @param id* @return*/FilesgetFileById(Stringid); /*** 根据id获取数据流* @param files* @return*/InputStreamgetFileInputStream(Filesfiles); }
fileServiceImpl实现类:
publicclassFileServiceImplimplementsFileService { "${file.save-path}") (privateStringsavePath; privateFileMapperfileMapper; publicResultupLoadFiles(MultipartFilefile) { //设置支持最大上传的文件,这里是1024*1024*2=2MlongMAX_SIZE=2097152L; //获取要上传文件的名称StringfileName=file.getOriginalFilename(); //如果名称为空,返回一个文件名为空的错误if (StringUtils.isEmpty(fileName)){ returnnewResult(ResponseCode.FILE_NAME_EMPTY.getCode(),ResponseCode.FILE_NAME_EMPTY.getMsg(),null); } //如果文件超过最大值,返回超出可上传最大值的错误if (file.getSize()>MAX_SIZE){ returnnewResult(ResponseCode.FILE_MAX_SIZE.getCode(),ResponseCode.FILE_MAX_SIZE.getMsg(),null); } //获取到后缀名StringsuffixName=fileName.contains(".") ?fileName.substring(fileName.lastIndexOf(".")) : null; //文件的保存重新按照时间戳命名StringnewName=System.currentTimeMillis() +suffixName; FilenewFile=newFile(savePath,newName); if (!newFile.getParentFile().exists()){ newFile.getParentFile().mkdirs(); } try { //文件写入file.transferTo(newFile); } catch (IOExceptione) { e.printStackTrace(); } //将这些文件的信息写入到数据库中Filesfiles=newFiles(newFile.getPath(),fileName,suffixName); fileMapper.insertFile(files); returnnewResult(ResponseCode.SUCCESS.getCode(),ResponseCode.SUCCESS.getMsg(),"数据上传成功"); } //根据id获取文件信息publicFilesgetFileById(Stringid) { Filesfiles=fileMapper.selectFileById(id); returnfiles; } //将文件转化为InputStreampublicInputStreamgetFileInputStream(Filesfiles) { Filefile=newFile(files.getFilePath()); try { returnnewFileInputStream(file); } catch (FileNotFoundExceptione) { e.printStackTrace(); } returnnull; } }
3.5 对数据库的操作
需要将数据写入到数据库中,这里用到的是mybatis,新建一个FileMapper接口:
publicinterfaceFileMapper { /*** 将数据信息插入到数据库* @param files*/voidinsertFile(Filesfiles); /*** 根据id获取文件* @param id* @return*/FilesselectFileById(Stringid); }
编写对应的xml文件
<mappernamespace="com.javayz.fileuploadanddownload.mapper.FileMapper"><insertid="insertFile"parameterType="com.javayz.fileuploadanddownload.entity.Files"> insert into file(filepath,filename,filesuffix) values(#{filePath},#{fileName},#{fileSuffix}); </insert><selectid="selectFileById"parameterType="string"resultType="com.javayz.fileuploadanddownload.entity.Files"> select * from file where id=#{id}; </select></mapper>
代码已上传至github,欢迎自取:github
(四)测试
将项目运行起来,首先测试文件上传,通过postman直接上传一个文件
点击send后得到操作成功的返回值,我们可以在自己设置的路径下看到这个文件,同时在数据库中也存在该文件的信息:
接下来测试文件下载,因为是get请求,直接在浏览器中访问:
http://localhost:8080/api/download/4即可调用下载。