文件上传下载功能开发v2

wxpay
LI-CCONG\李聪聪 8 months ago
parent 8ff3b2c30a
commit 76d39774a2

@ -1,11 +1,16 @@
package cc.yunxi.common.utils; package cc.yunxi.common.utils;
import cn.hutool.core.io.FileTypeUtil; import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil; import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.crypto.digest.DigestUtil;
import org.springframework.http.MediaType;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URLEncoder;
/** /**
* *
@ -13,21 +18,33 @@ import java.io.ByteArrayInputStream;
public class FileUtils { public class FileUtils {
/** /**
* *
* *
* @param content * @param content
* @param originalName * @param originalName
* @return path * @return path
*/ */
public static String generatePath(byte[] content, String originalName) { public static String generateName(byte[] content, String originalName) {
String sha256Hex = DigestUtil.sha256Hex(content); String md5Hex = DigestUtil.md5Hex(content);
// 情况一:如果存在 name则优先使用 name 的后缀 // 情况一:如果存在 name则优先使用 name 的后缀
if (StrUtil.isNotBlank(originalName)) { if (StrUtil.isNotBlank(originalName)) {
String extName = FileNameUtil.extName(originalName); String extName = FileNameUtil.extName(originalName);
return StrUtil.isBlank(extName) ? sha256Hex : sha256Hex + "." + extName; return StrUtil.isBlank(extName) ? md5Hex : md5Hex + "." + extName;
} }
// 情况二:基于 content 计算 // 情况二:基于 content 计算
return sha256Hex + '.' + FileTypeUtil.getType(new ByteArrayInputStream(content)); return md5Hex + '.' + FileTypeUtil.getType(new ByteArrayInputStream(content));
}
/**
*
*/
public static void downloadFile(HttpServletResponse response, byte[] content, String filename) throws IOException {
// 设置 header 和 contentType
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
// 输出资源
IoUtil.write(response.getOutputStream(), false, content);
} }
} }

@ -1,33 +1,16 @@
package cc.yunxi.controller; package cc.yunxi.controller;
import cc.yunxi.common.domain.CommonResult; import cc.yunxi.common.domain.CommonResult;
import cc.yunxi.common.exception.BizIllegalException;
import cc.yunxi.config.WxHsyProperties;
import cc.yunxi.domain.dto.UserDTO; import cc.yunxi.domain.dto.UserDTO;
import cc.yunxi.domain.dto.WxLoginDTO; import cc.yunxi.domain.dto.WxLoginDTO;
import cc.yunxi.domain.po.Recycler;
import cc.yunxi.domain.vo.common.FileUploadReqVO;
import cc.yunxi.enums.UserTypeEnum;
import cc.yunxi.service.IRecyclerService;
import cc.yunxi.service.impl.CommonService; import cc.yunxi.service.impl.CommonService;
import cc.yunxi.utils.JwtTool;
import cc.yunxi.utils.UserContext; import cc.yunxi.utils.UserContext;
import cc.yunxi.utils.WeChatUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.validation.Valid;
@Api(tags = "统一接口") @Api(tags = "统一接口")
@RestController @RestController

@ -1,33 +1,60 @@
package cc.yunxi.controller; package cc.yunxi.controller;
import cc.yunxi.common.domain.CommonResult; import cc.yunxi.common.domain.CommonResult;
import cc.yunxi.domain.vo.common.FileUploadReqVO; import cc.yunxi.common.exception.BizIllegalException;
import cc.yunxi.common.utils.FileUtils;
import cc.yunxi.domain.vo.file.FileUploadReqVO;
import cc.yunxi.domain.vo.file.FileUploadRespVO;
import cc.yunxi.service.IFileService; import cc.yunxi.service.IFileService;
import cn.hutool.core.util.StrUtil;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid; import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import java.io.IOException;
@Api(tags = "文件接口") @Api(tags = "文件接口")
@RestController @RestController
@RequestMapping("/file") @RequestMapping("/file")
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
@Validated
public class FileController { public class FileController {
private final IFileService fileService; private final IFileService fileService;
@ApiOperation("文件上传") @ApiOperation("文件上传")
@PostMapping(value ="/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value ="/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public CommonResult<String> upload(FileUploadReqVO uploadReqVO) { public CommonResult<FileUploadRespVO> upload(FileUploadReqVO uploadReqVO) {
fileService.uploadFile(uploadReqVO); FileUploadRespVO uploadRespVO = fileService.uploadFile(uploadReqVO);
return CommonResult.success("ok"); return CommonResult.success(uploadRespVO);
}
@ApiOperation("图片上传")
@PostMapping(value ="/uploadImg", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public CommonResult<FileUploadRespVO> uploadImg(FileUploadReqVO uploadReqVO) {
FileUploadRespVO uploadRespVO = fileService.uploadImage(uploadReqVO);
return CommonResult.success(uploadRespVO);
}
@ApiOperation("文件下载")
@GetMapping(value = "/download")
public void uploadImg(HttpServletResponse response, @NotBlank(message = "资源uri缺失") String url) {
// 读取内容
byte[] content = fileService.download(StrUtil.trim(url));
try {
FileUtils.downloadFile(response, content, "todo"); // 文件名todo
} catch (IOException e) {
throw new BizIllegalException("下载出错");
}
} }
} }

@ -1,21 +0,0 @@
package cc.yunxi.domain.vo.common;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Data
@ApiModel(description = "文件上传 request VO")
public class FileUploadReqVO {
@ApiModelProperty(value = "文件附件", required = true)
@NotNull(message = "文件附件不能为空")
private MultipartFile file;
@ApiModelProperty(value = "自定义文件路径", required = false, example = "bbj.png")
@NotEmpty(message = "文件路径不能为空")
private String path;
}

@ -0,0 +1,25 @@
package cc.yunxi.domain.vo.file;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotNull;
@Data
@ApiModel(description = "文件上传 request VO")
public class FileUploadReqVO {
@ApiModelProperty(value = "文件/图片", required = true)
@NotNull(message = "未上传资源")
private MultipartFile file;
// @ApiModelProperty(value = "业务类型", required = true)
// @NotNull(message = "文件附件不能为空")
// private String fileBusinessType;
// @ApiModelProperty(value = "自定义文件路径", hidden = true, example = "/xxx/yyyy/zzz")
// @NotNull(message = "文件路径不能为空")
// private String path;
}

@ -0,0 +1,22 @@
package cc.yunxi.domain.vo.file;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotNull;
@Data
@ApiModel(description = "文件上传 Response VO")
public class FileUploadRespVO {
@ApiModelProperty(value = "源文件名")
private String name;
@ApiModelProperty(value = "资源uri")
private String url;
@ApiModelProperty(value = "文件名")
private String field;
}

@ -0,0 +1,23 @@
package cc.yunxi.enums;
import cc.yunxi.common.enums.BaseEnum;
import cc.yunxi.common.enums.SwaggerDisplayEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*
*/
@Getter
@AllArgsConstructor
public enum FileStyleEnum implements BaseEnum {
DOC("1", "文件"),
IMAGE("2", "图片");
private final String code;
private final String desc;
}

@ -1,14 +1,11 @@
package cc.yunxi.service; package cc.yunxi.service;
import cc.yunxi.domain.po.Client;
import cc.yunxi.domain.query.ClientQuery; import cc.yunxi.domain.vo.file.FileUploadReqVO;
import cc.yunxi.domain.vo.client.ClientUpdateVO; import cc.yunxi.domain.vo.file.FileUploadRespVO;
import cc.yunxi.domain.vo.common.FileUploadReqVO;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import javax.validation.Valid; import javax.validation.Valid;
import java.io.IOException;
/** /**
* <p> * <p>
@ -20,5 +17,26 @@ import java.io.IOException;
*/ */
public interface IFileService { public interface IFileService {
void uploadFile(@Valid FileUploadReqVO uploadReqVO);
/**
*
* @param uploadReqVO
* @return FileUploadRespVO
*/
FileUploadRespVO uploadFile(@Valid FileUploadReqVO uploadReqVO);
/**
*
* @param uploadReqVO
* @return FileUploadRespVO
*/
FileUploadRespVO uploadImage(@Valid FileUploadReqVO uploadReqVO);
/**
*
*/
byte[] download(String url);
} }

@ -2,19 +2,25 @@ package cc.yunxi.service.impl;
import cc.yunxi.common.exception.BizIllegalException; import cc.yunxi.common.exception.BizIllegalException;
import cc.yunxi.common.utils.FileUtils; import cc.yunxi.common.utils.FileUtils;
import cc.yunxi.domain.vo.common.FileUploadReqVO; import cc.yunxi.domain.vo.file.FileUploadReqVO;
import cc.yunxi.domain.vo.file.FileUploadRespVO;
import cc.yunxi.enums.FileStyleEnum;
import cc.yunxi.service.IFileService; import cc.yunxi.service.IFileService;
import cn.hutool.core.io.FileTypeUtil; import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.io.unit.DataSizeUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.validation.Valid; import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.List;
import java.util.Optional;
/** /**
* <p> * <p>
@ -28,37 +34,84 @@ import java.io.InputStream;
@Validated @Validated
public class FileServiceImpl implements IFileService { public class FileServiceImpl implements IFileService {
@Value("${nxhs.upload.basePath}")
private String basePath;
@Value("${nxhs.upload.file.allowType}")
private List<String> fileAllowTypes;
@Value("${nxhs.upload.file.maxSize}")
private String fileMaxSize;
@Value("${nxhs.upload.image.allowType}")
private List<String> imageAllowTypes;
@Value("${nxhs.upload.image.maxSize}")
private String imageMaxSize;
@Override @Override
public void uploadFile(FileUploadReqVO uploadReqVO) { public FileUploadRespVO uploadFile(FileUploadReqVO uploadReqVO) {
MultipartFile file = uploadReqVO.getFile(); return upload(uploadReqVO.getFile(), FileStyleEnum.DOC);
String path = uploadReqVO.getPath(); }
@Override
public FileUploadRespVO uploadImage(FileUploadReqVO uploadReqVO) {
return upload(uploadReqVO.getFile(), FileStyleEnum.IMAGE);
}
@Override
public byte[] download(String url) {
// 默认为本地存储拉取 todo
String filePath = basePath + File.separator + url;
if (!FileUtil.exist(filePath)) {
throw new BizIllegalException("下载资源不存在");
}
return FileUtil.readBytes(filePath);
}
// 统一上传
private FileUploadRespVO upload(MultipartFile file, FileStyleEnum fileStyleEnum) {
// 获取文件源名称 // 获取文件源名称
String name = file.getOriginalFilename(); String originalName = file.getOriginalFilename();
try (InputStream inputStream = file.getInputStream()){ try (InputStream inputStream = file.getInputStream()){
// 获取文件二进制内容 // 获取文件二进制内容
byte[] content = IoUtil.readBytes(inputStream, false); byte[] content = IoUtil.readBytes(inputStream, false);
// 获取文件类型 // 文件类型、大小校验
String fileType = FileTypeUtil.getType(inputStream); String fileType = FileTypeUtil.getType(inputStream, originalName);
// 文件类型、大小校验 todo if (fileStyleEnum.equals(FileStyleEnum.DOC)) {
long fileSize = file.getSize(); if (!fileAllowTypes.contains(fileType.toLowerCase())) {
throw new BizIllegalException("文件类型不正确");
if (StrUtil.isEmpty(path)) { // 不自定义path时 }
path = FileUtils.generatePath(content, name); if (file.getSize() > DataSizeUtil.parse(fileMaxSize)) {
throw new BizIllegalException("文件大小限制" + fileMaxSize);
}
} else {
if (!imageAllowTypes.contains(fileType.toLowerCase())) {
throw new BizIllegalException("图片格式不正确");
}
if (file.getSize() > DataSizeUtil.parse(imageMaxSize)) {
throw new BizIllegalException("图片大小限制" + fileMaxSize);
}
} }
// 生成md5文件名
// 源文件name为空时则使用 path 填充 String fileName = FileUtils.generateName(content, originalName);
if (StrUtil.isEmpty(name)) { // 判断文件是否存在,如果存在直接返回 todo
name = path; // path = Optional.ofNullable(path).orElse(basePath) + File.separator;
} // 确定上传路径
String URI = File.separator + fileStyleEnum.toString().toLowerCase() + File.separator + fileName;
// 上传到文件存储位置 todo String fullPath = basePath + URI;
FileUtil.writeBytes(content, path); // 上传到文件存储位置
FileUtil.writeBytes(content, fullPath);
FileUploadRespVO uploadRespVO = new FileUploadRespVO();
uploadRespVO.setName(originalName);
uploadRespVO.setField(fileName);
uploadRespVO.setUrl(URI);
return uploadRespVO;
// 入库操作 todo // 入库操作 todo
} catch (IOException exception) { } catch (IOException exception) {
throw new BizIllegalException("文件上传失败"); throw new BizIllegalException("上传失败");
} }
} }
} }

@ -4,7 +4,11 @@ server:
spring: spring:
application: application:
name: nxhs-service name: nxhs-service
servlet:
multipart: #文件传输配置
max-file-size: 100MB #单个数据大小限制
max-request-size: 100MB #请求总数据大小限制
enabled: true #是否启用分段上传支持
profiles: profiles:
active: dev active: dev
datasource: datasource:
@ -69,6 +73,14 @@ nxhs:
- /api/common/hsylogin - /api/common/hsylogin
- /api/common/shlogin - /api/common/shlogin
- /api/test/** - /api/test/**
upload:
basePath: ${user.dir}/upload/
file:
allowType: doc,docx,xls,xlsx,pdf
maxSize: 100MB
image:
allowType: jpg,jpeg,png,gif
maxSize: 5MB
wx: wx:
client: # 散户端 client: # 散户端
appid: wx630bc4f43990c80c appid: wx630bc4f43990c80c

Loading…
Cancel
Save