springboot文件上傳和下載(批量/單個)

前言

用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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章