第2-3-4章 上傳附件的介面開發-文件存儲服務系統-nginx/fastDFS/minio/阿里雲oss/七牛雲oss

5.3 介面開發-上傳附件

第2-1-2章 傳統方式安裝FastDFS-附FastDFS常用命令
第2-1-3章 docker-compose安裝FastDFS,實現文件存儲服務
第2-1-5章 docker安裝MinIO實現文件存儲服務-springboot整合minio-minio全網最全的資料

全套程式碼及資料全部完整提供,點此處下載

5.3.1 介面文檔

上傳附件介面要完成的操作主要有兩個:

  • 將客戶端提交的文件上傳到指定存儲位置(具體存儲位置由配置文件配置的存儲策略確定)
  • 將上傳的文件資訊保存到資料庫的pd_attachment表中

介面文檔如下:
在這裡插入圖片描述
在這裡插入圖片描述

5.3.2 程式碼實現

第一步:創建AttachmentController並提供文件上傳方法

package com.itheima.pinda.file.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.pinda.base.BaseController;
import com.itheima.pinda.base.R;
import com.itheima.pinda.file.dto.AttachmentDTO;
import com.itheima.pinda.file.dto.AttachmentRemoveDTO;
import com.itheima.pinda.file.dto.AttachmentResultDTO;
import com.itheima.pinda.file.dto.FilePageReqDTO;
import com.itheima.pinda.file.entity.Attachment;
import com.itheima.pinda.file.service.AttachmentService;
import com.itheima.pinda.utils.BizAssert;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
import static com.itheima.pinda.exception.code.ExceptionCode.BASE_VALID_PARAM;
import static java.util.stream.Collectors.groupingBy;
/**
 * 附件處理控制器
 */
@RestController
@RequestMapping("/attachment")
@Slf4j
@Api(value = "附件", tags = "附件")
public class AttachmentController extends BaseController {
    @Autowired
    private AttachmentService attachmentService;

    /**
     * 上傳附件
     */
    @ApiOperation(value = "附件上傳", notes = "附件上傳")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "isSingle", value = "是否單文件", dataType = "boolean", paramType = "query"),
            @ApiImplicitParam(name = "id", value = "文件id", dataType = "long", paramType = "query"),
            @ApiImplicitParam(name = "bizId", value = "業務id", dataType = "long", paramType = "query"),
            @ApiImplicitParam(name = "bizType", value = "業務類型", dataType = "long", paramType = "query"),
            @ApiImplicitParam(name = "file", value = "附件", dataType = "MultipartFile", allowMultiple = true, required = true),
    })
    @PostMapping(value = "/upload")
    public R<AttachmentDTO> upload(
            @RequestParam(value = "file") MultipartFile file,
            @RequestParam(value = "isSingle", required = false, defaultValue = "false") Boolean isSingle,
            @RequestParam(value = "id", required = false) Long id,
            @RequestParam(value = "bizId", required = false) String bizId,
            @RequestParam(value = "bizType") String bizType) {
        
        // 忽略路徑欄位,只處理文件類型
        if (file.isEmpty()) {
            return fail(BASE_VALID_PARAM.build("請求中必須至少包含一個有效文件"));
        }
        AttachmentDTO attachment = attachmentService.upload(file, 
                                                            id, 
                                                            bizType, 
                                                            bizId, 
                                                            isSingle);

        return success(attachment);
    }
}

第二步:創建AttachmentService介面

package com.itheima.pinda.file.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.pinda.file.dto.AttachmentDTO;
import com.itheima.pinda.file.dto.AttachmentResultDTO;
import com.itheima.pinda.file.dto.FilePageReqDTO;
import com.itheima.pinda.file.entity.Attachment;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * 附件業務介面
 */
public interface AttachmentService extends IService<Attachment> {
    /**
     * 上傳附件
     *
     * @param file 文件
     * @param id 附件id
     * @param bizType 業務類型
     * @param bizId 業務id
     * @param isSingle 是否單個文件
     * @return
     */
    AttachmentDTO upload(MultipartFile file, Long id, String bizType, 
                         String bizId, Boolean isSingle);

}

第三步:創建AttachmentServiceImpl實現類

package com.itheima.pinda.file.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.pinda.base.id.IdGenerate;
import com.itheima.pinda.database.mybatis.conditions.Wraps;
import com.itheima.pinda.database.mybatis.conditions.query.LbqWrapper;
import com.itheima.pinda.dozer.DozerUtils;
import com.itheima.pinda.exception.BizException;
import com.itheima.pinda.file.dao.AttachmentMapper;
import com.itheima.pinda.file.domain.FileDO;
import com.itheima.pinda.file.domain.FileDeleteDO;
import com.itheima.pinda.file.dto.AttachmentDTO;
import com.itheima.pinda.file.dto.AttachmentResultDTO;
import com.itheima.pinda.file.dto.FilePageReqDTO;
import com.itheima.pinda.file.entity.Attachment;
import com.itheima.pinda.file.entity.File;
import com.itheima.pinda.file.enumeration.DataType;
import com.itheima.pinda.file.properties.FileServerProperties;
import com.itheima.pinda.file.service.AttachmentService;
import com.itheima.pinda.file.strategy.FileStrategy;
import com.itheima.pinda.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 附件業務實現類
 */
@Slf4j
@Service
public class AttachmentServiceImpl extends ServiceImpl<AttachmentMapper, Attachment> implements AttachmentService {
    @Autowired
    private IdGenerate<Long> idGenerate;
    @Autowired
    private DozerUtils dozer;
    @Autowired
    private FileStrategy fileStrategy;
    @Autowired
    private FileServerProperties fileProperties;

    /**
     * 上傳附件
     *
     * @param multipartFile 文件
     * @param id 附件id
     * @param bizType 業務類型
     * @param bizId 業務id
     * @param isSingle 是否單個文件
     * @return
     */
    @Override
    public AttachmentDTO upload(MultipartFile multipartFile, Long id, String bizType, String bizId, Boolean isSingle) {
        //根據業務類型來判斷是否生成業務id
        if (StringUtils.isNotEmpty(bizType) && StringUtils.isEmpty(bizId)) {
            bizId = String.valueOf(idGenerate.generate());
        }
        File file = fileStrategy.upload(multipartFile);

        Attachment attachment = dozer.map(file, Attachment.class);

        attachment.setBizId(bizId);
        attachment.setBizType(bizType);
        setDate(attachment);

        if (isSingle) {
            super.remove(Wraps.<Attachment>lbQ().eq(Attachment::getBizId, bizId).eq(Attachment::getBizType, bizType));
        }

        if (id != null && id > 0) {
            //當前端傳遞了文件id時,修改這條記錄
            attachment.setId(id);
            super.updateById(attachment);
        } else {
            attachment.setId(idGenerate.generate());
            super.save(attachment);
        }

        AttachmentDTO dto = dozer.map(attachment, AttachmentDTO.class);
        return dto;
    }

    private void setDate(Attachment file) {
        LocalDateTime now = LocalDateTime.now();
        file.setCreateMonth(DateUtils.formatAsYearMonthEn(now));
        file.setCreateWeek(DateUtils.formatAsYearWeekEn(now));
        file.setCreateDay(DateUtils.formatAsDateEn(now));
    }
}

5.3.3 介面測試

第一步:啟動Nacos配置中心,pd-file-server.yml配置內容如下:

pinda:
  mysql:
    database: pd_files
  nginx:
    ip: ${spring.cloud.client.ip-address} #正式環境要將該ip設置成nginx對應的公網ip
    port: 10000  #正式環境需要將該ip設置成nginx對應的公網埠
  swagger:
    enabled: true
    docket:
      file:
        title: 文件服務
        base-package: com.itheima.pinda.file.controller

  file:
    type: LOCAL # LOCAL ALI MINIO FAST_DFS  
    local:
      uriPrefix: //${pinda.nginx.ip}:${pinda.nginx.port}
      bucket-name: oss-file-service
      endpoint: D:\soft\nginx-1.23.0\uploadFiles
    ali:
      # 請填寫自己的阿里雲存儲配置
      bucket-name: bladex-loan
      endpoint: //oss-cn-qingdao.aliyuncs.com
      access-key-id: LTAI4FhtimFAiz6iLGJSiJui  
      access-key-secret: SsU15qaPwpF1x5xMqwc0XzGuY92fnc

#FAST_DFS配置
fdfs:
  soTimeout: 1500
  connectTimeout: 600
  thumb-image:
    width: 150
    height: 150
  tracker-list:
    - 111.231.76.210:22122
  pool:
    #從池中借出的對象的最大數目
    max-total: 153
    max-wait-millis: 102
    jmx-name-base: 1
    jmx-name-prefix: 1


server:
  port: 8765

第二步:啟動Nginx服務

第三步:啟動文件服務

第四步:訪問介面文檔,地址為//localhost:8765/doc.html

在這裡插入圖片描述

可以看到上傳的文件已經保存到了本地磁碟:
在這裡插入圖片描述
同時上傳的文件資訊也已經保存到了pd_attachment表中:
在這裡插入圖片描述

可以通過Nginx提供的Http服務來訪問上傳的文件:

//192.168.137.3:10000//oss-file-service/2022/11/e76d3505-df38-4f95-a7bd-fb5de3ebe923.txt

全套程式碼及資料全部完整提供,點此處下載