SpringBoot+Nginx大文件传输

Nginx配置

				# 公众端的附件上传
        location /api/visitor/upload {
            # Pass altered request body to this location
            upload_pass   /api/outerPortal/uploadAndSave;

            # Store files to this directory
            # The directory is hashed, subdirectories 0 1 2 3 4 5 6 7 8 9 should exist
            upload_store /home/museum/data/fileupload/upload/visitor 1;
            
            # Allow uploaded files to be read only by user
            upload_store_access user:rw;

            # Set specified fields in request body
            upload_set_form_field "${upload_field_name}_name" $upload_file_name;
            upload_set_form_field "${upload_field_name}_content_type" $upload_content_type;
            upload_set_form_field "${upload_field_name}_path" $upload_tmp_path;

            # Inform backend about hash and size of a file
            upload_aggregate_form_field "${upload_field_name}_md5" $upload_file_md5;
            upload_aggregate_form_field "${upload_field_name}_size" $upload_file_size;
			
			upload_pass_form_field ".*";
			upload_pass_form_field "modelName";

        }

        # Pass altered request body to a backend
        location /api/outerPortal/uploadAndSave {
           proxy_pass  http://10.130.1.11:8250;
        }

Springboot接口接受


package xxxx.museum.outerPortal.interfaces;

import xxxx.common.domain.model.Result;
import xxxx.common.domain.model.SessionUser;
import xxxx.museum.outerPortal.domain.model.FileStoreItemVisitor;
import xxxx.museum.outerPortal.service.UploadFileService;
import xxxx.visitor.common.VisitorBaseController;
import xxxx.visitor.common.domain.model.VisitorSessionUser;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 平台common文件上传暴露接口
 */
@RestController
@ConditionalOnClass(value = {org.thymeleaf.dialect.IExpressionObjectDialect.class})
@RequestMapping("api/outerPortal")
public class UploadController extends VisitorBaseController {

    @Autowired
    private UploadFileService uploadFileService;

    /**
     * 接收普通附件的上传
     */
    @PostMapping("/uploadAndSave")
    @ResponseBody
    @ApiOperation(value = "上传文件", notes = "用户文件上传")
    public Result uploadAndSave(@RequestParam(value = "modelName", required = false) String modelName,
                                @RequestParam(value = "files_name", required = false) String fileName,
                                @RequestParam(value = "files_path", required = false) String filePath,
                                @RequestParam(value = "files_size", required = false) Integer fileSize,
                                HttpServletRequest request,
                                HttpServletResponse reponse) {
        Result msg = new Result();
        msg.setSuccess(false);
        VisitorSessionUser sessionUser = getSessionUser(request);
        //将得到的用户对象
        SessionUser user = new SessionUser();
        user.setUserId(sessionUser.getMid());
        if (StringUtils.isNotEmpty(fileName)) {
            FileStoreItemVisitor itemVisitor = uploadFileService.save(sessionUser, modelName, fileName, filePath, fileSize);
            if (itemVisitor != null) {
                msg.setResponse(itemVisitor);
                msg.setMsg("上传头像成功!");
                msg.setSuccess(true);
            } else {
                msg.setMsg("上传失败,请重新上传!");
            }
        } else {
            msg.setMsg("上传失败,请重新上传!");
        }
        return msg;
    }


}

实现上传的机制

package xxxx.museum.outerPortal.service;

import cn.hutool.core.util.RuntimeUtil;
import com.alibaba.nacos.common.utils.UuidUtils;
import xxxx.common.domain.model.exception.BusinessException;
import xxxx.museum.outerPortal.domain.model.FileStoreItemVisitor;
import xxxx.visitor.common.domain.model.VisitorSessionUser;
import org.im4java.core.ConvertCmd;
import org.im4java.core.IMOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.Calendar;
import java.util.Date;

/**
 * @author Administrator
 * @apiNote 公众端文件上传
 * @date 2024/6/5 14:20
 */
@Service
@SuppressWarnings("all")
public class UploadFileService {
    private static String forbiddenExtension = "sh,bat,exe";

    private final String fileSaveBasePath = "/home/museum/data/fileupload/common/";

    private static String[] allowPictures = "bmp,jpeg,jpg,png".split(",");
    private Logger logger = LoggerFactory.getLogger(getClass());

    @Value("${visitor.html.base.path:http://10.130.1.11/fileupload/common/}")
    private String htmlBasePath;


    @Value("${visitor.image.magick.path:C:\\GraphicsMagick-1.3.36-Q16}")
    private String imageMagickPath;

    /**
     * 过滤有害脚本文件
     *
     * @param fileExt fileExt
     * @return boolean
     */
    private boolean checkFileExtension(String fileExt) {
        String[] forbiddenTypes = forbiddenExtension.split(",");
        String[] var3 = forbiddenTypes;
        int var4 = forbiddenTypes.length;

        for (int var5 = 0; var5 < var4; ++var5) {
            String s = var3[var5];
            if (s.equalsIgnoreCase(fileExt)) {
                return false;
            }
        }

        return true;
    }

    public FileStoreItemVisitor save(VisitorSessionUser sessionUser, String modelName, String fileName, String filePath, Integer fileSize) {
        String fileExtension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
        if (!this.checkFileExtension(fileExtension)) {
            throw new BusinessException("不允许上传此类型文件:" + fileExtension);
        } else {
            // 年月日路径
            String relativePath = this.getRelativeFolder(fileSaveBasePath);
            // 生成本次文件移动的地址
            String folderPath = fileSaveBasePath + relativePath;
            // 生成文件名称
            String saveFileName = UuidUtils.generateUuid() + "." + fileExtension;
            // 生成全限定名称的文件声明
            String saveFilePath = folderPath + saveFileName;
            // Nginx Upload 文件 由暂存地址移动到当前路径全限定名
            String result = RuntimeUtil.execForStr(new String[]{"mv", filePath, saveFilePath});
            this.logger.info("文件移动结果:" + result);
            FileStoreItemVisitor fileStoreItem = new FileStoreItemVisitor();
            // 生成Nginx可访问的相对路径
            fileStoreItem.setRelativeUrl(relativePath);
            // 生成预览压缩图地址
            fileStoreItem.setThumbUrl(this.generateThumbnail(htmlBasePath, saveFilePath, fileExtension));
            logger.info("文件 Nginx 缩略图 路径:" + fileStoreItem.getThumbUrl());
            fileStoreItem.setFileName(fileName);
            fileStoreItem.setFileType(fileExtension);
            fileStoreItem.setFileSize(fileSize);
            fileStoreItem.setCreateTime(new Date());
            fileStoreItem.setCreatorId(sessionUser.getUserId());
            fileStoreItem.setCreator(sessionUser.getUserName());
            // 生成下载地址
            fileStoreItem.setHtmlUrl(htmlBasePath + relativePath + saveFileName);
            logger.info("文件 Nginx 下载 路径:" + fileStoreItem.getHtmlUrl());
            return fileStoreItem;
        }

    }

    private String generateThumbnail(String htmlBasePath, String filePath, String fileExtension) {
        String thumbnailUrl = "";
        if (this.isPicture(fileExtension)) {
            int idx = filePath.lastIndexOf(".");
            if (idx > -1) {
                String thumbnailPath = filePath.substring(0, idx) + "_thb." + fileExtension;
                this.compressImage(filePath, thumbnailPath, 300, 300, 0.8F);
                thumbnailUrl = htmlBasePath + thumbnailPath.replace(fileSaveBasePath, "");
            }
        } else {
            thumbnailUrl = "/static/global/img/file/" + fileExtension.toLowerCase() + ".png";
        }

        return thumbnailUrl;
    }


    public void compressImage(String imagePath, String thumbnailPath, int width, int height, Float quality) {
        IMOperation op = new IMOperation();
        op.addImage();
        String raw = width + "x" + height + "^";
        ConvertCmd cmd = new ConvertCmd(true);
        op.addRawArgs(new String[]{"-sample", raw});
        if (quality != null && !quality.toString().equals("")) {
            op.addRawArgs(new String[]{"-quality", quality.toString()});
        }
        op.addImage();
        String osName = System.getProperty("os.name").toLowerCase();
        if (osName.indexOf("win") != -1) {
            cmd.setSearchPath(imageMagickPath);
        } else {
            cmd.setSearchPath("/usr/local/bin");
        }

        try {
            cmd.run(op, new Object[]{imagePath, thumbnailPath});
        } catch (Exception var12) {
            this.logger.error("文件压缩失败", var12);
        }

    }

    boolean isPicture(String fileExtension) {
        String[] var2 = allowPictures;
        int var3 = var2.length;
        for (int var4 = 0; var4 < var3; ++var4) {
            String s = var2[var4];
            if (s.equalsIgnoreCase(fileExtension)) {
                return true;
            }
        }

        return false;
    }

    private String getRelativeFolder(String fileSavePath) {
        String relativePath = this.getFolderByDate();
        String path = fileSavePath + relativePath;
        File tempPath = new File(path);
        if (!tempPath.exists()) {
            tempPath.mkdirs();
        }
        return relativePath;
    }


    private String getFolderByDate() {
        StringBuilder builder = new StringBuilder();
        Calendar now = Calendar.getInstance();
        builder.append(now.get(1)).append("/").append(now.get(2) + 1).append("/").append(now.get(5)).append("/");
        return builder.toString();
    }
}

结果

http://10.130.1.11/fileupload/common/2024/6/5/5694f15f-b345-4efd-a97e-4774dd38f467.xlsx
{
  "@type": "com.cztech.common.domain.model.Result",
  "msg": "上传头像成功!",
  "obj": {
    "@type": "com.cztech.museum.outerPortal.domain.model.FileStoreItemVisitor",
    "createTime": "2024-06-05 17:51:35",
    "creatorId": "2365",
    "fileName": "琴.jpeg",
    "fileSize": 95263,
    "fileType": "jpeg",
    "htmlUrl": "http://10.130.1.11/fileupload/common/2024/6/5/",
    "relativeUrl": "2024/6/5/",
    "thumbUrl": "http://10.130.1.11/fileupload/common//home/museum/data/fileupload/common/2024/6/5/724440db-d1af-48dc-a67a-5e124e78b566_thb.jpeg"
  },
  "response": {
    "@type": "com.cztech.museum.outerPortal.domain.model.FileStoreItemVisitor",
    "createTime": "2024-06-05 17:51:35",
    "creatorId": "2365",
    "fileName": "琴.jpeg",
    "fileSize": 95263,
    "fileType": "jpeg",
    "htmlUrl": "http://10.130.1.11/fileupload/common/2024/6/5/",
    "relativeUrl": "2024/6/5/",
    "thumbUrl": "http://10.130.1.11/fileupload/common/2024/6/5/724440db-d1af-48dc-a67a-5e124e78b566_thb.jpeg"
  },
  "success": true
}

image

原图

image

压缩图

image

posted on 2024-06-05 18:12  白嫖老郭  阅读(33)  评论(0编辑  收藏  举报

导航