最近踩的雷你中招了吗?spring boot ftp 生成二维码、上传附件以及回显

273 阅读5分钟

环境: centos、jdk1.8、vsftpd、nginx、spring boot、docker ftp上传附件,上传的附件有两种方式回显,在下面再详细说明 此处省略ftp服务器、docker服务器nginx服务器搭建过程。 上传首先在application.yml文件中添加ftp配置

ftp:

内网 000.000.000.000 外网 111.111.111.111

ip: 111.111.111.111

ip: 000.000.000.000 name: ftp_user password: ftp_user base: path: /home/ftp_user

访问ftp文件地址,这里配的是nginx地址

file: url: http://ip1:port/ftp/

设置上传文件的大小,我是通过在yml文件中配置的,还有一种是通过www.cnblogs.com/fswhq/p/136…

spring: servlet: multipart: enabled: true max-file-size: 102400 #设置时是1024的整数倍 max-request-size: 1000MB

一、通过ftp生成二维码,回显地址结合nginx

import com.google.zxing.BarcodeFormat;

/**

  • @author lz

  • @description 二维码构建 */ public class QRCode {

    /**

    • 二维码文本 */ private final String text;

    /**

    • 图片宽度 */ private final int width;

    /**

    • 图片高度 */ private final int height;

    /**

    • 图片存储路径 */ private final String imgPath;

    /**

    • 图片类型 */ private final String imgType;

    /**

    • 二维码类型 */ private final BarcodeFormat type;

    public QRCode(Builder builder){ this.text = builder.text; this.width = builder.width; this.height = builder.height; this.imgPath = builder.imgPath; this.imgType = builder.imgType; this.type = builder.type; }

    public String getText() { return text; }

    public int getWidth() { return width; }

    public int getHeight() { return height; }

    public String getImgPath() { return imgPath; }

    public String getImgType() { return imgType; }

    public BarcodeFormat getType() { return type; }

    public static class Builder {

     public static final String         PNG = "png";
    
     public static final String         JPEG = "jpeg";
    
     public static final String         JPG = "jpg";
    
     public static final String         BMP = "bmp";
    
     /**
      * 二维码文本
      */
     private String          text;
    
     /**
      * 图片宽度
      */
     private int             width = 260;
    
     /**
      * 图片高度
      */
     private int             height = 260;
    
     /**
      * 图片存储路径
      */
     private String          imgPath;
    
     /**
      * 图片类型
      */
     private String          imgType = PNG;
    
     /**
      * 二维码类型
      */
     private BarcodeFormat type = BarcodeFormat.QR_CODE;
    
     /**
      * 设置文本
      * @param text
      * @return
      */
     public Builder text(String text){
         this.text = text;
         return this;
     }
    
     /**
      * 设置图片宽度
      * @param width
      * @return
      */
     public Builder width(int width){
         this.width = width;
         return this;
     }
    
     /**
      * 设置图片高度
      * @param height
      * @return
      */
     public Builder height(int height){
         this.height = height;
         return this;
     }
    
     /**
      * 设置图片保存路径
      * @param imgPath
      * @return
      */
     public Builder imgPath(String imgPath){
         this.imgPath = imgPath;
         return this;
     }
    
     /**
      * 设置图片类型
      * @param imgType
      * @return
      */
     public Builder imgType(String imgType){
         this.imgType = imgType;
         return this;
     }
    
     /**
      * 设置二维码类型(支持条形码)
      * @param type
      * @return
      */
     public Builder type(BarcodeFormat type){
         this.type = type;
         return this;
     }
    
     public QRCode build(){
         return new QRCode(this);
     }
    

    } }

import com.ctsi.attachment.controller.AttachmentController; import com.ctsi.ssdc.util.MatrixToImageWriter; import com.google.zxing.EncodeHintType; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.QRCodeWriter; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component;

import java.io.*; import java.util.Hashtable; import java.util.StringTokenizer;

/**

  • @author lz

  • @description */ @Component public class QRCodeClient {

    private QRCodeClient() { // nothing to do }

    @Value("${ftp.ip}") private final String ftpIp;

    @Value("${ftp.name}") private final String ftpName;

    @Value("${ftp.password}") private final String ftpPassword;

    @Value("${ftp.base.path}") private final String ftpBasePath;

    /**

    • 环境变量 */ @Autowired private Environment environment;

// @Value("${qr_server.path}") private String QR_CODE_PATH = "/qrcode";

@Value("${file.url}")
private String fileUrl;

private final Logger logger = LoggerFactory.getLogger(AttachmentController.class);

public String generateQrcodeUrl(String code) {
    String fileName = code + "-qrcode";
    //返回图片地址
    return generate(new QRCode.Builder().text(code)
            .imgType(QRCode.Builder.PNG).build(), fileName);
}

/**
 * 生成二维码图片
 *
 * @param qrcode 二维码对象
 * @return 地址
 */
public String generate(QRCode qrcode, String fileName) {
    FTPClient ftp = new FTPClient();
    try {
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix = qrCodeWriter.encode(qrcode.getText(), qrcode.getType(), qrcode.getWidth(), qrcode.getHeight());
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        MatrixToImageWriter.writeToStream(bitMatrix, qrcode.getImgType(), out);
        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
        ftp.connect(ftpIp, 21);//设置地址和端口号
        logger.info("登陆前");
        ftp.login(ftpName, ftpPassword);//用户名和密码
        ftp.enterLocalPassiveMode();
        ftp.setFileType(FTPClient.BINARY_FILE_TYPE);//上传文件类型 二进制文件
        int reply = ftp.getReplyCode();
        logger.info("reply:"+reply);
        if (!FTPReply.isPositiveCompletion(reply)) {//检查连接是否有效
            System.out.println("error");
        }
        StringTokenizer s = new StringTokenizer(ftpBasePath + QR_CODE_PATH, "/");
        s.countTokens();
        String pathName = "";
        logger.info("创建路径前");
        while (s.hasMoreElements()) {
            pathName = pathName + "/" + (String) s.nextElement();
            try {
                ftp.mkd(pathName);
            } catch (Exception e) {
                logger.info("ftp文件夹创建失败");
            }
        }
        logger.info("路径创建后,打印当前工作目录:"+ftp.printWorkingDirectory());
        ftp.changeWorkingDirectory(ftpBasePath + QR_CODE_PATH);

        boolean boo = ftp.storeFile(fileName + "." + qrcode.getImgType().toLowerCase(), in);//关键代码,把流持久化到硬盘上

        return fileUrl+"qrcode/".concat(fileName + "." + qrcode.getImgType().toLowerCase()).replaceAll("\\\\", "/");
    } catch (Exception e) {
        return null;
    }
}

} 说明:测试直接调用 QRCodeClient类中的 generateQrcodeUrl方法,参数是想要通过扫码展示的二维码内容,返回的是通过nginx做的二维码地址,或是可以在本地搭建一个tomcat,然后把二维码生成在webapps里面,通过tomcat访问 二、通过ftp上传文件,回显地址是结合nginx

import cn.hutool.core.date.DateUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest; import java.io.FileInputStream; import java.util.*;

/**

  • @Auther: lz

  • @Description:文件上传 */ @RestController @RequestMapping("/attachment") @Slf4j public class AttachmentController {

    /**

    • 日志输出 */ private final Logger logger = LoggerFactory.getLogger(AttachmentController.class);

    @Value("${ftp.ip}") private String ftpIp;

    @Value("${ftp.name}") private String ftpName;

    @Value("${ftp.password}") private String ftpPassword;

    @Value("${ftp.base.path}") private String ftpBasePath;

    private static final String STR_TXT = ".txt";

    private static final String STR_DOC = ".doc";

    private static final String STR_DOCX = ".docx";

    private static final String STR_XLS = ".xls";

    private static final String STR_XLSX = ".xlsx";

    private static final String STR_PPTX = ".pptx";

    private static final String STR_PPT = ".ppt";

    private static final String STR_ZIP = ".zip";

    private static final String STR_RAR = ".rar";

    private static final String STR_JPG = ".jpg";

    private static final String STR_PNG = ".png";

    /**

    • 附件表-上传 */ @PostMapping("/upload") public Result upload(HttpServletRequest request,@RequestParam("file") MultipartFile importFile) { Result result = new Result();

      // 获取当前文件名

      String fileName = importFile.getOriginalFilename(); String suffix = fileName.substring(fileName.lastIndexOf(".")); String[] strings = {STR_DOC, STR_DOCX, STR_JPG, STR_PNG, STR_PPT, STR_PPTX, STR_RAR, STR_TXT, STR_ZIP, STR_XLSX, STR_XLS}; Set suffixSet = new HashSet<>(Arrays.asList(strings)); logger.info("文件后缀:"+suffix); if ((!suffixSet.contains(suffix))) { logger.info("后缀判断!"); throw new RestControllerException(ATTACH_WRONG_FORMAT.getMsg(),new Throwable(ATTACH_WRONG_FORMAT.getMsg())); }

      String date = DateUtil.format(new Date(),"yyyyMMdd");

      Long time = System.currentTimeMillis(); fileName = time + suffix;

      try { // 文件流写入服务器中 // FileUtil.saveFile(importFile.getInputStream(), calssUrl, fileName); String basePath = + "/" + date + "/" + time; FTPClient ftp = new FTPClient(); logger.info("文件上传ftp连接前"); // 文件流写入服务器中 ftp.connect(ftpIp, 21);//设置地址和端口号 logger.info("登陆前"); ftp.login(ftpName, ftpPassword);//用户名和密码 logger.info("登陆后"); ftp.enterLocalPassiveMode();//这里有坑!!!!! ftp.setFileType(FTPClient.BINARY_FILE_TYPE);//上传文件类型 二进制文件 int reply = ftp.getReplyCode(); logger.info("reply:"+reply); if (!FTPReply.isPositiveCompletion(reply)) {//检查连接是否有效 System.out.println("error"); } StringTokenizer s = new StringTokenizer(ftpBasePath + basePath, "/"); s.countTokens(); String pathName = ""; logger.info("创建路径前"); while (s.hasMoreElements()) { pathName = pathName + "/" + (String) s.nextElement(); try { ftp.mkd(pathName); } catch (Exception e) { log.info("ftp文件夹创建失败"); } } logger.info("路径创建后,打印当前工作目录:"+ftp.printWorkingDirectory()); ftp.changeWorkingDirectory(ftpBasePath + basePath); FileInputStream fis= (FileInputStream) importFile.getInputStream(); logger.info("文件持久到服务器前 获取的文件大小:"+fis.available()+";"); boolean boo = ftp.storeFile(importFile.getOriginalFilename(), fis);//关键代码,把流持久化到硬盘上 logger.info("文件保存服务器状态:"+boo); fis.close(); ftp.logout(); logger.info("ftp登出后"); ftp.disconnect(); logger.info("ftp取消连接后"); } catch (Exception e) { logger.info("ftp报错"); throw new RestControllerException(ATTACH_UPLOAD_FAIL.getMsg(),new Throwable(ATTACH_UPLOAD_FAIL.getMsg())); }

      String url = ("http://ip1:port/ftp/"+ date + "/" + time+"/"+ importFile.getOriginalFilename()).replaceAll("\\", "/");

      result.setData(url) return result; }

} 说明:

ftp.enterLocalPassiveMode();//这里是一个大坑

由于服务器设置的有白名单所有项目发布到服务器上时需要把ftp_ip改为内网地址并且需要enterLocalPassiveMode。使用外网访问ftp时不需要做enterLocalPassiveMode设置,本地测试时,本地电脑是作为服务器的,一般不会设置防护火墙。

三、文件上传成功后,通过实现WebMvcConfigure接口,addResourceHandle方法设置静态文件映射,来访问ftp上的文件

import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.util.ResourceUtils; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.io.File; import java.io.FileNotFoundException;

/**

  • @Auther: lz
  • @Description:
  • @Date: 2020/10/30 */

@Configuration public class UploadFilePathConfig implements WebMvcConfigurer { /** * 日志输出 */ private final Logger logger = LoggerFactory.getLogger(UploadFilePathConfig.class);

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    String os = System.getProperty("os.name");
    File path = null;
    try {

//获取classpath目录的完整路径 path = new File(ResourceUtils.getURL("classpath:").getPath()); } catch (FileNotFoundException e) { e.printStackTrace(); } logger.info("添加外部资源映射位置1"+path.getParentFile().getParentFile().getPath()); String gitPath=path.getParentFile().getParentFile().getParent()+File.separator+"ftp_user"+File.separator+"qrcode"+File.separator; logger.info("添加外部资源映射位置2:"+gitPath); //前面一个参数是请求路径,后面一个是文件在服务器的绝对路径 registry.addResourceHandler("/static/upload/**").addResourceLocations(gitPath); }

@Override
public void addCorsMappings(CorsRegistry registry){
    logger.info("添加外部资源映射请求添加跨域");
    registry.addMapping("/static/upload/**")
            .allowedHeaders("*")
            .allowedMethods("POST","GET","PATCH")
            .allowedOrigins("*");
}

}

说明:使用这个方法时需要把上面提到的ip1:port改为后台接口的请求路径 文档中提到的ftp服务器、nginx服务器、docker搭建网上有很多,或是以后有时间会继续补充, 这个是最近开发时踩的雷,更多的雷希望互相学习,如果你觉得文章还不错,欢迎关注收藏。

作者:mooncharmzx 链接:blog.bccn.net/mooncharmzx…