推荐一个思路非常简单又很实用的文件上传下载方式,代码十分简练,可以开箱即用,下面是使用到的一些工具类和业务代码;
1.文件上传实现
判断文件类型的工具类,一些使用到的实体类我会凡在文末,需要可以的自取
public static int fileType(String fileName) {if (fileName == null) {return 0;} else {// 获取文件后缀名并转化为写,用于后续比较String fileType = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();// 创建图片类型数组String img[] = {"bmp", "jpg", "jpeg", "png", "tiff", "gif", "pcx", "tga", "exif", "fpx", "svg", "psd","cdr", "pcd", "dxf", "ufo", "eps", "ai", "raw", "wmf"};for (int i = 0; i < img.length; i++) {if (img[i].equals(fileType)) {return 1;}}// 创建文档类型数组String document[] = {"txt", "doc", "docx", "xls", "htm", "html", "jsp", "rtf", "wpd", "pdf", "ppt"};for (int i = 0; i < document.length; i++) {if (document[i].equals(fileType)) {return 2;}}// 创建视频类型数组String video[] = {"mp4", "avi", "mov", "wmv", "asf", "navi", "3 gp", "mkv", "f4v", "rmvb", "webm"};for (int i = 0; i < video.length; i++) {if (video[i].equals(fileType)) {return 3;}}// 创建音乐类型数组String music[] = {"mp3", "wma", "wav", "mod", "ra", "cd", "md", "asf", "aac", "vqf", "ape", "mid", "ogg","m4a", "vqf"};for (int i = 0; i < music.length; i++) {if (music[i].equals(fileType)) {return 4;}}}return 0;}
1.2 Controller层代码
部分小伙伴因为是springboot项目且版本大于2.3,@Valid注解报错找不到,需要手动添加依赖,如果对“优雅的Java参数校验”感兴趣的伙伴可以见我另一篇文章优雅的参数校验@Validated 实战 + 统一异常处理返回前端json 最全解析
<dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId><version>2.0.1.Final</version></dependency>
@Api(tags = "附件管理")
@RestController
@RequestMapping("/attach")
public class AttachmentController {@Autowiredprivate AttachmentService attachmentService;@PostMapping("/upload")@ApiOperation(value = "上传文件")public ResponseResult upload(@RequestPart("file") MultipartFile file,@RequestParam(value = "bussinessId") @Valid @NotBlank(message = "业务id不能为空") String bussinessId) throws Exception {return attachmentService.upload(file, bussinessId);}
1.3 Service层业务代码
接口类
import com.yuncheng.entity.ResponseResult;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletResponse;public interface AttachmentService {ResponseResult upload(MultipartFile file, String bussinessId) throws Exception;}
实现类,注释很清晰了
import com.yuncheng.entity.ResponseResult;
import com.yuncheng.entity.enums.AppHttpCodeEnum;
import com.yuncheng.pc.entity.UploadFile;
import com.yuncheng.pc.mapper.UploadFileMapper;
import com.yuncheng.pc.service.AttachmentService;
import com.yuncheng.utils.Tools;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;import lombok.extern.slf4j.Slf4j;@Slf4j
@Service
public class AttachmentServiceImpl implements AttachmentService {private static final String LOCAL_HOST = "http://10.11.0.104:8866/";@Autowiredprivate UploadFileMapper uploadFileMapper;@Override@Transactionalpublic ResponseResult upload(MultipartFile multipartFile, String bussinessId) throws Exception {if (multipartFile.isEmpty()) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"上传文件为空");}UploadFile uploadFile = new UploadFile();// 保存原文件名称,文件列表展示需要用到uploadFile.setFileName(multipartFile.getOriginalFilename());// 生成系统文件名称,不可重复,防止同名文件上传覆盖问题String name = RandomStringUtils.randomAlphanumeric(32) + multipartFile.getOriginalFilename().substring(multipartFile.getOriginalFilename().lastIndexOf(".")).toLowerCase();uploadFile.setName(name.substring(0, name.lastIndexOf(".")));uploadFile.setFileSuffix(multipartFile.getOriginalFilename().substring(multipartFile.getOriginalFilename().lastIndexOf(".")).toLowerCase());// 判断文件类型int fileType = Tools.fileType(name);uploadFile.setType(fileType);uploadFile.setDomain(LOCAL_HOST + "resource/files/");//这种方式比类型强转效率更高String pathExt = System.currentTimeMillis()+"";uploadFile.setPath(LOCAL_HOST + "resource/files/" + pathExt);uploadFile.setPathExt(pathExt);// 获取文件大小uploadFile.setSize((int)multipartFile.getSize());uploadFile.setBussinessId(bussinessId);uploadFile.setStatus(1);uploadFile.setCreateTime(new Date());uploadFile.setId(Tools.getCode32());uploadFileMapper.insert(uploadFile);// 将文件保存到本目录/resources/files/下// DateUtil.today()得到得是当天日期如:20230715,这个会在/resources/files/下再以日期生成一层目录File newFile = new File("./resources/files/"+ pathExt + "/" + name);// 保证这个文件的父文件夹必须要存在if (!newFile.getParentFile().exists()) {newFile.getParentFile().mkdirs();}newFile.createNewFile();// 将文件内容写入到这个文件中InputStream is = multipartFile.getInputStream();FileOutputStream fos = new FileOutputStream(newFile);try {int len;byte[] buf = new byte[1024];while ((len = is.read(buf)) != -1) {fos.write(buf, 0, len);}} finally {// 关流顺序,先打开的后关闭fos.close();is.close();}// 返回文件信息给前端Map resultMap = new HashMap();resultMap.put("id", uploadFile.getId());resultMap.put("path", uploadFile.getPath());return ResponseResult.okResult(resultMap);}
}
1.4 Mapper层代码
import com.yuncheng.pc.entity.UploadFile;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.List;@Mapper
public interface UploadFileMapper {void insert(@Param("uploadFile") UploadFile uploadFile);
}
对应sql语句
<insert id="insert">insert into "SYSDBA"."ATTACHMENT"("ID", "CREATE_BY", "CREATE_TIME", "STATUS", "UPDATE_BY","UPDATE_TIME", "NAME", "FILE_NAME", "FILE_SUFFIX", "TYPE","DOMAIN", "PATH", "PATH_EXT", "SIZE", "BUSSINESS_ID")VALUES(#{uploadFile.id},#{uploadFile.createBy},#{uploadFile.createTime},#{uploadFile.status},#{uploadFile.updateBy},#{uploadFile.updateTime},#{uploadFile.name},#{uploadFile.fileName},#{uploadFile.fileSuffix},#{uploadFile.type},#{uploadFile.domain},#{uploadFile.path},#{uploadFile.pathExt},#{uploadFile.size},#{uploadFile.bussinessId})</insert>
1.5 测试功能
至此文件上传代码就写好了,我们去postman测试一下,记住选post类型且文件类型要选对
点击发送即上传成功
2 文件下载代码
2.1 controller层代码
@GetMapping("/download")@ApiOperation(value = "下载文件")public void download(HttpServletResponse response,@Valid @NotBlank(message = "id不能为空") String id) throws Exception {attachmentService.download(response, id);}
2.2 Service层代码
接口
ResponseResult download(HttpServletResponse response, String id) throws Exception;
实现类
@Overridepublic ResponseResult download(HttpServletResponse response, String id) throws Exception {List<UploadFile> uploadFiles = uploadFileMapper.selectOne(id, null, 1);if (CollectionUtils.isEmpty(uploadFiles)) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "查询文件为空");}// 去./resources/files/目录下取出文件File downloadFile = new File("./resources/files/" +uploadFiles.get(0).getPathExt() + "/" + uploadFiles.get(0).getName() + uploadFiles.get(0).getFileSuffix());if (!downloadFile.exists() || downloadFile.length() == 0) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "查询文件为空");}InputStream is = null;OutputStream os = null;try {// 判断是否是图片,如果是图片加上 response.setContentType("image/jpeg"),这样就可以直接在浏览器打开而不是下载if (uploadFiles.get(0).getType() == 1) {response.setContentType("image/jpeg");}response.addHeader("Content-Length", "" + downloadFile.length());is = new FileInputStream(downloadFile);os = response.getOutputStream();IOUtils.copy(is, os);} catch (Exception e) {log.error("下载图片发生异常", e);} finally {try {if (os != null) {os.flush();os.close();}if (is != null) {is.close();}} catch (IOException e) {log.error("关闭流发生异常", e);}}return ResponseResult.okResult("下载成功");}
2.3 Mapper层代码
List<UploadFile> selectOne(@Param("id") String id, @Param("bussinessId") String bussinessId, @Param("status") Integer status);
对应SQL语句
<select id="selectOne" resultType="com.yuncheng.pc.entity.UploadFile">select *from "SYSDBA"."ATTACHMENT"where status = #{status}<if test="id != null and id != ''">and id = #{id}</if><if test="bussinessId != null and bussinessId != ''">and bussiness_id = #{bussinessId}</if></select>
2.4 测试功能
下面是一些使用到的实体类和建表语句,有需要的小伙伴可以自取,如果觉得文章对您有用的话记得点赞 收藏 关注哦!!!,主页全是实用文章待你来取!
文件上传实体类
import lombok.Data;import java.util.Date;@Data
public class UploadFile {private String id;//文件在系统中的名称private String name;//文件名称private String fileName;//文件后缀private String fileSuffix;//文件类型private Integer type;//主目录private String domain;//完整目录private String path;//扩展目录private String pathExt;//文件大小private Integer size;//关联的业务idprivate String bussinessId;//逻辑删除状态private Integer status;private Date createTime;private String createBy;private Date updateTime;private String updateBy;
}
统一结果返回bean
import com.yuncheng.entity.enums.AppHttpCodeEnum;
import lombok.Data;import java.io.Serializable;/*** 通用的结果返回类* @param <T>*/
@Data
public class ResponseResult<T> implements Serializable {private String token;private Integer code;private String message;private T data;private String status;public ResponseResult() {this.code = 200;}public ResponseResult(Integer code, T data) {this.code = code;this.data = data;}public ResponseResult(Integer code, String msg, T data) {this.code = code;this.message = msg;this.data = data;}public ResponseResult(Integer code, String msg) {this.code = code;this.message = msg;}public static ResponseResult okResult(int code, String msg) {ResponseResult result = new ResponseResult();return result.ok(code, null, msg);}public static ResponseResult okResult(int code, Object data,String msg) {ResponseResult result = new ResponseResult();return result.ok(code, data, msg);}public static ResponseResult okResult(Object data) {ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getMessage());if(data!=null) {result.setData(data);}return result;}public static ResponseResult errorResult(int code, String msg) {ResponseResult result = new ResponseResult();return result.error(code, msg);}public static ResponseResult errorResult(AppHttpCodeEnum enums){return setAppHttpCodeEnum(enums,enums.getMessage());}public static ResponseResult errorResult(AppHttpCodeEnum enums, String message){return setAppHttpCodeEnum(enums,message);}public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums){return okResult(enums.getCode(),enums.getMessage());}private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums, String message){return okResult(enums.getCode(),message);}public ResponseResult<?> error(Integer code, String msg) {this.code = code;this.message = msg;return this;}public ResponseResult<?> ok(Integer code, T data) {this.code = code;this.data = data;return this;}public ResponseResult<?> ok(Integer code, T data, String msg) {this.code = code;this.data = data;this.message = msg;return this;}public ResponseResult<?> ok(T data) {this.data = data;return this;}
}
状态码枚举类
package com.yuncheng.entity.enums;public enum AppHttpCodeEnum {// 成功段固定为200SUCCESS(200,"操作成功"),// 登录段1~50NEED_LOGIN(1,"需要登录后操作"),LOGIN_PASSWORD_ERROR(2,"密码错误"),// TOKEN50~100TOKEN_INVALID(50,"无效的TOKEN"),TOKEN_EXPIRE(51,"TOKEN已过期"),TOKEN_REQUIRE(52,"TOKEN是必须的"),// SIGN验签 100~120SIGN_INVALID(100,"无效的SIGN"),SIG_TIMEOUT(101,"SIGN已过期"),// 参数错误 500~1000PARAM_REQUIRE(500,"缺少参数"),PARAM_INVALID(501,"无效参数"),PARAM_IMAGE_FORMAT_ERROR(502,"图片格式有误"),SERVER_ERROR(503,"服务器内部错误"),// 数据错误 1000~2000DATA_EXIST(1000,"数据已经存在"),AP_USER_DATA_NOT_EXIST(1001,"ApUser数据不存在"),DATA_NOT_EXIST(1002,"数据不存在"),DATA_DUPLICATE(1003,"数据重复"),OPERATION_FAILED(1004,"操作失败"),// 数据错误 3000~3500NO_OPERATOR_AUTH(3000,"无权限操作"),NEED_ADMIND(3001,"需要管理员权限");int code;String message;AppHttpCodeEnum(int code, String errorMessage){this.code = code;this.message = errorMessage;}public int getCode() {return code;}public String getMessage() {return message;}
}
建表语句
CREATE TABLE "SYSDBA"."ATTACHMENT"
(
"ID" VARCHAR2(64) NOT NULL,
"CREATE_BY" VARCHAR(64),
"CREATE_TIME" TIMESTAMP(0),
"STATUS" INTEGER,
"UPDATE_BY" VARCHAR(64),
"UPDATE_TIME" TIMESTAMP(0),
"NAME" VARCHAR(100),
"FILE_NAME" VARCHAR(100),
"FILE_SUFFIX" VARCHAR(50),
"TYPE" INTEGER,
"DOMAIN" CHARACTER(100),
"PATH" VARCHAR(100),
"PATH_EXT" VARCHAR(50),
"SIZE" INTEGER,
"BUSSINESS_ID" VARCHAR(100) NOT NULL) STORAGE(ON "MAIN", CLUSTERBTR) ;COMMENT ON TABLE "SYSDBA"."ATTACHMENT" IS '附件表';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."BUSSINESS_ID" IS '业务id';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."CREATE_BY" IS '创建人';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."CREATE_TIME" IS '创建日期';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."DOMAIN" IS '主目录';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."FILE_NAME" IS '原文件名';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."FILE_SUFFIX" IS '文件后缀';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."ID" IS '业务id';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."NAME" IS '系统文件名';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."PATH" IS '路径';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."PATH_EXT" IS '扩展目录';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."SIZE" IS '文件大小';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."STATUS" IS '删除标志';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."TYPE" IS '类型';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."UPDATE_BY" IS '更新人';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."UPDATE_TIME" IS '修改时间';