版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
前言
用springboot + jpa,批量上传、下载,一般批量下载处理方式都是将多个文件压缩成一个压缩包,然后在下载,主要的坑在于本地上传和下载都正常,部署在服务器上会出现各种错误,比如找不到路径,下载文件失败等等。
文件压缩工具类
见之前写的处理相关乱码的文章: https://blog.csdn.net/qq_39150374/article/details/93715504
实体类
实体类中有关联关联的id,实际项目中一般上传下载都不会是一张表
package com.example.student.entity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;
/**
* 描述:文件上传下载
*/
@Table(
name = "Document"
)
@Entity
@Getter
@Setter
@DynamicInsert//新增时空字段不去插入values
@DynamicUpdate//只跟新变化的字段,结合merge方法使用
@JsonInclude(JsonInclude.Include.NON_NULL)
@EntityListeners({AuditingEntityListener.class})
@JsonIgnoreProperties({"modifier", "modifyDate"})
public class Document implements BaseEntity {
@Id
@GeneratedValue
private Integer id;
@Column(columnDefinition = "int default 0")
private Integer companyId;//企业id
@Column(columnDefinition = "varchar(100) default ''")
private String name;//文件名
@Column(columnDefinition = "varchar(100) default ''")
private String path;//文件路径
@Column(columnDefinition = "varchar(20) default ''")
private String fileType;//文件类型
@Column(columnDefinition = "decimal(10,0) default 0")
private BigDecimal fileSize;//文件大小
@Column(columnDefinition = "int default 0")
private Integer downloadTimes;//下载次数
@Column(columnDefinition = "varchar(20) default ''")
private String creatorName;//创建人
@CreatedBy
private Integer creator;
@CreatedDate
private Date createDate;
@LastModifiedBy
private Integer modifier;
@LastModifiedDate
private Date modifyDate;
}
Controller层
package com.example.student.controller;
import com.example.student.criteria.DocumentCriteria;
import com.example.student.response.BaseEntity;
import com.example.student.service.DocumentServiceImpl;
import com.example.student.util.PageBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.List;
/**
* 描述:文件管理
*/
@RestController
@RequestMapping("api/document")
public class DocumentController extends BaseController {
@Autowired
DocumentServiceImpl documentService;
/**
* 文件上传
*
* @param objId 所属id
* @param request 文件request
* @return
*/
@PostMapping("/upload")
public Object fileUpload(Integer objId, MultipartHttpServletRequest request) {
return BaseEntity.success(documentService.fileUpload(objId, request));
}
/**
* 单文件下载
*
* @param id 文件id
* @return
*/
@GetMapping("/download/{id}")
public void fileDownload(@PathVariable Integer id, HttpServletRequest request, HttpServletResponse response) {
documentService.downloadAlone(id, request, response);
}
/**
* 批量文件下载
*
* @param ids 文件id集合
* @return
*/
@PostMapping("/download")
public void fileMultiDownload(@RequestBody Collection<Integer> ids, HttpServletRequest request, HttpServletResponse response) {
documentService.downloadMulti(ids, request, response);
}
@DeleteMapping("")
public Object delete(@RequestBody List<Integer> ids) {
return BaseEntity.success(documentService.delete(ids));
}
}
service层
package com.example.student.service;
import com.example.student.criteria.DocumentCriteria;
import com.example.student.e_num.DocumentType;
import com.example.student.entity.Document;
import com.example.student.entity.DocumentRelations;
import com.example.student.repository.CommonRepository;
import com.example.student.repository.DocumentRelationsRepository;
import com.example.student.repository.DocumentRepository;
import com.example.student.response.ResException;
import com.example.student.util.DateUtil;
import com.example.student.util.Helpers;
import com.example.student.util.PageBean;
import com.example.student.util.ZipFilesUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
/**
* 描述:文件管理
*/
@Service
public class DocumentServiceImpl {
@Autowired
private CommonRepository commonRepository;
@Autowired
private DocumentRepository documentRepository;
@Autowired
private DocumentRelationsRepository documentRelationsRepository;
@Value("${fileProps.filePath}")
private String filePath;
/**
* 文件上传类型
*/
private final static List<String> fileTypes = new ArrayList<String>(){{
add("doc");
add("docx");
add("xls");
add("xlsx");
add("ppt");
add("pptx");
add("txt");
add("md");
add("pdf");
add("jpg");
add("jpeg");
add("png");
add("gif");
add("bmp");
}};
/**
* 文件上传
*
* @param objId 文件所属id
* @param request 请求
* @return
*/
public List<Integer> fileUpload(Integer objId, MultipartHttpServletRequest request) {
//按月来保存一个目录
String uploadPath = DateUtil.dateToString(new Date(), DateUtil.YEAR_NO_DAY);
//文件路径不存在,则新建
String checkPath = filePath + "/" + uploadPath;
File fileStream = new File(checkPath);
if (!fileStream.exists()) {
fileStream.mkdirs();
}
//开始上传
MultiValueMap<String, MultipartFile> multiFileMap = request.getMultiFileMap();
List<MultipartFile> files = new ArrayList<>();
for (Map.Entry<String, List<MultipartFile>> temp : multiFileMap.entrySet()) {
files.addAll(temp.getValue());
}
List<Document> documents = new ArrayList<>();
Document document;
BufferedOutputStream stream;
for (MultipartFile file : files) {
if (file.isEmpty()) {
throw new ResException("存在文件为空,请确认!");
}
try {
document = new Document();
//文件属性
String fileName = file.getOriginalFilename();
BigDecimal fileSize = BigDecimal.valueOf(file.getSize());
int len = fileName.lastIndexOf(".");
if (len <= 0) {
throw new ResException("文件类型有误,请确认!");
}
String fileType1 = fileName.substring(len + 1).toLowerCase();
if (!fileTypes.contains(fileType1)) {
throw new ResException("只允许上传word、excel、ppt、pdf、txt和图片!");
}
String name = fileName.substring(0, len);
String fileType = fileName.substring(len + 1);
String physicalName = (new SimpleDateFormat("yyyyMMddHHmmssSSS")).format(new Date()) + "_" + fileName;
String path = uploadPath + "/" + physicalName;
document.setName(name);
document.setFileSize(fileSize);
document.setFileType(fileType);
document.setPath(path);
document.setDownloadTimes(0);
byte[] bytes = file.getBytes();
stream = new BufferedOutputStream(new FileOutputStream(new File(checkPath + "/" + physicalName)));
stream.write(bytes);
stream.close();
documents.add(document);
} catch (IOException e) {
e.printStackTrace();
}
}
documentRepository.save(documents);
List<Integer> documentIds = documents.stream().map(Document::getId).collect(Collectors.toList());
//需要一个标识标识文件属于企业文件
String isCompany = request.getParameter("isCompany");
Boolean flag = Boolean.valueOf(isCompany);
if (flag) {
Helpers.requireNonNull("企业参数有误,联系管理员", objId);
List<DocumentRelations> relationsList = new ArrayList<>();
DocumentRelations relations;
for (Integer documentId : documentIds) {
relations = new DocumentRelations();
relations.setDocumentId(documentId);
relations.setInfoId(objId);
relations.setInfoType(DocumentType.COMPANY.getValue());
relationsList.add(relations);
}
documentRelationsRepository.save(relationsList);
}
return documentIds;
}
@Transactional
public void downloadAlone(Integer id, HttpServletRequest request, HttpServletResponse response) {
Document document = documentRepository.findOne(id);
Helpers.requireNonNull("当前文件不存在,请联系管理员", document);
File file = new File(filePath + "/" + document.getPath());
if (!file.exists()) {
throw new ResException("文件 :" + document.getName() + "不存在,请联系管理员!");
}
document.setDownloadTimes(document.getDownloadTimes() == null ? 1 : document.getDownloadTimes() + 1);
String originFileName = document.getName() + "." + document.getFileType();
ZipFilesUtil.downloadFile(file, originFileName, request, response);
}
@Transactional
public void downloadMulti(Collection<Integer> ids, HttpServletRequest request, HttpServletResponse response) {
List<Document> documents = documentRepository.findAllByIdIn(ids);
List<File> files = new ArrayList<>();
for (Document document : documents) {
File file = new File(filePath + "/" + document.getPath());
if (file.exists()) {
files.add(file);
} else {
throw new ResException("文件 :" + document.getName() + "不存在,请联系管理员!");
}
document.setDownloadTimes(document.getDownloadTimes() == null ? 1 : document.getDownloadTimes() + 1);
}
if (files.isEmpty()) {
throw new ResException("当前选择文件不存在,请联系管理员!");
} else {
String tempName = "temp.zip";
String path = filePath + "/" + tempName;
//压缩
ZipFilesUtil.createZipFiles(files, path, response);
//下载
ZipFilesUtil.downloadFile(new File(path), tempName, request, response);
}
}
/**
* 删除
*/
@Transactional
public Integer delete(List<Integer> ids) {
if (ids.isEmpty())
return 0;
documentRelationsRepository.deleteByInfoIdInAndInfoType(ids, DocumentType.COMPANY.getValue());
return documentRepository.deleteAllByIdIn(ids);
}
}
Repository
package com.example.student.repository;
import com.example.student.entity.Document;
import org.springframework.data.repository.CrudRepository;
import java.util.Collection;
import java.util.List;
/**
* 描述:文件管理
*/
public interface DocumentRepository extends CrudRepository<Document, Integer> {
List<Document> findAllByIdIn(Collection<Integer> ids);
Integer deleteAllByIdIn(Collection<Integer> ids);
}
package com.example.student.repository;
import com.example.student.entity.DocumentRelations;
import org.springframework.data.repository.CrudRepository;
import java.util.Collection;
import java.util.List;
/**
* 描述:附件关联关系
*/
public interface DocumentRelationsRepository extends CrudRepository<DocumentRelations, Integer> {
void deleteByInfoIdInAndInfoType(Collection<Integer> infoIds, Integer infoType);
List<DocumentRelations> findAllByInfoType(Integer infoType);
List<DocumentRelations> findByInfoIdAndInfoType(Integer infoId, Integer infoType);
}
注意事项
yml部分配置
http:
multipart:
max-file-size: 10Mb # 单个文件不超过10M
max-request-size: 100Mb # 请求文件总大小不超过100M
# 文件存储位置
fileProps:
filePath: /user/student/files