前言
用springboot + jpa,批量上傳、下載,一般批量下載處理方式都是將多個文件壓縮成一個壓縮包,然後在下載,主要的坑在於本地上傳和下載都正常,部署在服務器上會出現各種錯誤,比如找不到路徑,下載文件失敗等等。
文件壓縮工具類
見之前寫的處理相關亂碼的文章: springboot處理批量文件下載文件名和內容亂碼問題
實體類
實體類中有關聯關聯的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