【技术碎片】jcraft.jsch 文件传输工具与方法

716 阅读9分钟

前言

SFTP相当于SSH + FTP,其中FTP则协议通常用来在两个服务器之间传输文件,但是它本质上是不安全的。SSH 是较可靠,专为远程登录会话和其他网络服务提供安全性的协议。因此SFTP也就是安全的网络文件传输协议。

Jsch进行服务器连接时首先需要实例化一个jsch对象,再利用这个对象根据用户名,主机ip,端口获取一个Session对象,设置好相应的参数后,进行连接,创建连接后,这个session一直可用不需要关闭,之后我们需要在session上建立channel通道。

本文记录基于jcraft.jsch的文件传输工具与方法

依赖

基于springboot,mybatis-plus,PostgreSQL,jcraft.jsch

 	<dependency>
		<groupId>com.jcraft</groupId>
		<artifactId>jsch</artifactId>
        <version>0.1.49</version>
	 </dependency>

ChannelSftp功能

ChannelSftp类是JSch实现SFTP核心类,它包含了所有SFTP的方法,如:

put(): 文件上传

get(): 文件下载

cd(): 进入指定目录

ls(): 得到指定目录下的文件列表

rename(): 重命名指定文件或目录

rm(): 删除指定文件

mkdir(): 创建目录

rmdir(): 删除目录

JSch支持三种文件传输模式:

  1. OVERWRITE

    完全覆盖模式,这是JSch的默认文件传输模式,即如果目标文件已经存在,传输的文件将完全覆盖目标文件,产生新的文件。

  2. RESUME

    恢复模式,如果文件已经传输一部分,这时由于网络或其他任何原因导致文件传输中断,如果下一次传输相同的文件,则从上一次中断的地方续传。

  3. APPEND

    追加模式,如果目标文件已存在,传输的文件将在目标文件后追加。

实现

JschUtil.java

我们定义JschUtil.java 的 ftp工具类,并定义方法


import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.UUID;

/**
 * JschUtil ftp(file transport protocol) 文件传输方法 
 * @author 
 * @since 2022-05-17
 */
@Slf4j
public class JschUtil {

    /**
     * ChannelSftp
     */
    public static ChannelSftp channelSftp;

    public static JSch jSch;

    public static Session session;

    public static Properties sshConfig;

    public static Channel channel;

    /**
     * 建立ftp连接
     * @param ftpAddress
     * @param ftpPort
     * @param ftpUsername
     * @param ftpPassword
     * @param ftpBasePath
     * @return
     * @throws IOException
     */
    public static Boolean connect(String ftpAddress,
                                  int ftpPort,
                                  String ftpUsername,
                                  String ftpPassword,
                                  String ftpBasePath) throws IOException {
        // 判断是否成功的boolean值
        boolean success = false;
        try {
            // 创建JSch对象
            jSch = new JSch();
            // 调用JSch对象的getSession方法(参数是服务器的访问用户名,服务器访问路径,服务器的端口号)给session赋值
            session = jSch.getSession(ftpUsername, ftpAddress, ftpPort);
            // 给session对象设置密码参数也就是你的服务器访问的密码
            session.setPassword(ftpPassword);
            // 创建参数
            sshConfig = new Properties();
            // 给参数对象赋值,这里解决
            sshConfig.put("StrictHostKeyChecking", "no");
            // 这里设置参数给session主要解决把kerberos认证方式去掉,如果不写这一步走到这里你需要向控制台输入你的,kerberos用户名和口令,如果你的项目环境没有涉及到kerberos应该是不用设置
            session.setConfig("PreferredAuthentications", "publickey,keyboard-interactive,password");
            // 把参数对象给session对象注入
            session.setConfig(sshConfig);
            // 打开session连接
            session.connect(15000);
            // 使用session对象连接服务器
            channel = session.openChannel("sftp");
            channel.connect();
            log.info("连接图片服务器:" + ftpAddress + "  连接用户名为:" + ftpUsername);
            channelSftp = (ChannelSftp) channel;
            // 使用ChannelSftp对象进行使用命令
            // 进入需要进入的路径
            channelSftp.cd(ftpBasePath);
            // 设置上传成功
            success = true;
        } catch (JSchException | SftpException e) {
            log.error(e.getMessage(), e);
            e.printStackTrace();
        }
        return success;
    }

    /**
     * 断开ftp连接
     */
    public static void disConnect() {
        if (channelSftp != null && channelSftp.isConnected()) {
            channelSftp.disconnect();
            log.info("断开channelSftp连接");
        }
        if (channel != null && channel.isConnected()) {
            channel.disconnect();
            log.info("断开channel连接");
        }
        if (session != null && session.isConnected()) {
            session.disconnect();
            log.info("断开session连接");
        }
    }

    /**
     * 重名文件检查与过滤
     * 如果存在重名文件,将被重命名(加 _1, _2, ... 后缀)
     * @param originalFileName 文件全名
     * @return String
     */
    public static String filterSameNameFile(String originalFileName) {
        // 文件是否存在重名
        Boolean repeat = JschUtil.isFileExist(originalFileName);

        // 如果存在重名,则加(1)后缀
        if (repeat) {
            StringBuilder fileName = new StringBuilder();
            int suffixNumber = 0;
            String fileExtensionName = "";

            // 分离文件拓展名(.jpg .png ... )
            for (int i = originalFileName.length() - 1; i > 0; i--) {
                if (originalFileName.charAt(i) == '.') {
                    fileExtensionName = originalFileName.substring(i, originalFileName.length());
                    fileName = new StringBuilder(originalFileName.substring(0, i));
                    break;
                }
            }

            // 加_1后缀
            fileName.append("_" + ++suffixNumber);
            repeat = JschUtil.isFileExist(fileName.toString() + fileExtensionName);
            // 如果已经存在 _1 后缀,则改为 _2 , _3 ,...直到文件名不重复为止
            while (repeat) {
                // 把我们添加的"_"后面所有数字字符删除
                for (int length = fileName.length(); length > 0; length--) {
                    char c = fileName.charAt(length - 1);
                    if (c != '_') {
                        fileName.delete(length - 1, length);
                    }
                    else {
                        break;
                    }
                }
                // 在"_"后面添加suffixNumber
                fileName.append(++suffixNumber);
                // 文件名查重,repeat = false则退出循环
                repeat = JschUtil.isFileExist(fileName.toString() + fileExtensionName);
            }
            // 合并扩展名后返回
            fileName.append(fileExtensionName);
            return fileName.toString();
        }

        // 不存在重名,直接返回
        return originalFileName;
    }


    /**
     * 判断文件或目录是否存在
     * @param name 文件名
     * @return boolean
     */
    public static boolean isFileExist(String name) {
        boolean isExist = false;
        try {
            channelSftp.lstat(name);
            isExist = true;
        } catch (Exception e) {
            if (e.getMessage().toLowerCase().equals(Constants.NOT_SUCH_FILE)) {
                isExist = false;
            }
        }
        return isExist;
    }


    /**
     * 上传图片到ftp服务器,支持文件重名加后缀
     * @param file 图片文件
     * @param pathPrefix nginx图片服务器地址前缀
     * @return String
     * @throws IOException
     */
    public static String ftpUpload(MultipartFile file, String pathPrefix, String fileName) throws IOException {
        // 获取到文件的文件名
//        String fileName = file.getOriginalFilename();
        // 根据文件名 + UUID生产新的文件名
//        String newFileName = UUID.randomUUID() + ".png";

        String newFileName = fileName;
        // 判断是否成功的boolean值
        boolean success = false;
        // 从MultipartFile对象中获取流
        InputStream inputStream = file.getInputStream();
        // 返回值为图片访问路径
        String path = "";
        try {
            // 进行文件上传
            channelSftp.put(inputStream, newFileName);
            // 设置上传成功
            success = true;
        } catch (SftpException e) {
            e.printStackTrace();
        }
        // 判断是否成功
        if (success) {
            // 设置返回路径为访问路径(你的服务器访问路径+新文件名)
            path = pathPrefix + newFileName;
            log.info("图片上传成功, 地址: " + path);
        }
        return path;
    }


    /**
     * ftp下载方法
     * @param fileName 文件名
     * @param localPath 本地下载路径
     * @return Boolean
     * @throws IOException
     */
    public static Boolean ftpDownload(String fileName, String localPath) throws IOException {
        // 判断是否成功的boolean值
        boolean success = false;

        try {
            // 进行文件下载
            channelSftp.get(fileName, localPath);
            // 设置上传成功
            success = true;
        } catch (SftpException e) {
            e.printStackTrace();
        }
        // 判断是否成功
        if (success){
            log.info("图片 " + fileName + " 下载成功");
        }
        return success;
    }


    public static void main(String[] args) {

        Boolean repeat = true;
        String originalFileName = "1232121421412(0).jpg";
        // 文件是否存在重名
        StringBuilder fileName = new StringBuilder();

        // 如果存在重名,则加(1)后缀
        if (repeat) {
            int suffixNumber = 0;
            String fileExtensionName = "";

            // 分离文件拓展名(.jpg .png ... )
            for (int i = originalFileName.length() - 1; i > 0; i--) {
                if (originalFileName.charAt(i) == '.') {
                    fileExtensionName = originalFileName.substring(i, originalFileName.length());
                    fileName = new StringBuilder(originalFileName.substring(0, i));
                    break;
                }
            }

            // 加_1后缀
            fileName.append("_" + ++suffixNumber);
            // 如果已经存在 _1 后缀,则改为 _2 , _3 ,...直到不重复为止
            while (repeat) {
                // 把我们添加的"_"后面所有数字字符删除
                for (int length = fileName.length(); length > 0; length--) {
                    char c = fileName.charAt(length - 1);
                    if (c != '_') {
                        fileName.delete(length - 1, length);
                    }
                    else {
                        break;
                    }
                }
                fileName.append(++suffixNumber);

                // 假设_23时不再重复
                if ("23".equals(fileName.substring(fileName.length() - 2, fileName.length()))) {
                    break;
                }
            }
            // 合并扩展名后返回
            fileName.append(fileExtensionName);
            System.out.println(fileName);
        }

    }
}

ImageInfoService.java

图片上传下载接口定义

package org.sample.service;

import com.baomidou.mybatisplus.extension.service.IService;
import org.sample.common.request.PageRequest;
import org.sample.common.response.Response;
import org.sample.dao.entity.ImageInfo;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * 图片信息服务接口
 * @author 
 * @since 2022-06-07
 */
public interface ImageInfoService extends IService<ImageInfo> {

    Response saveImage(MultipartFile image, List<String> authorizedUsers);

    Response saveImages(HttpServletRequest httpServletRequest, List<String> authorizedUsers);

    Response imagesPagingPreview(PageRequest pageRequest);

    Response updateImageInfo(String imageAddress, List<String> authorizedUsers);

    Response downloadImage(String imageAddress, String downloadPath);
}

ImageInfoServiceImpl.java

图片传输接口实现类

package org.sample.service.Impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.sample.common.enums.IsDeletedEnum;
import org.sample.common.request.PageRequest;
import org.sample.common.response.PageResponse;
import org.sample.common.response.Response;
import org.sample.common.utils.EncrUtil;
import org.sample.common.utils.JschUtil;
import org.sample.common.utils.ResponseGenerator;
import org.sample.dao.entity.ImageInfo;
import org.sample.dao.mapper.ImageInfoServiceMapper;
import org.sample.service.ImageInfoService;
import org.sample.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static org.sample.common.utils.JschUtil.ftpDownload;

/**
 * 图片信息服务实现类
 * @author 
 * @since 2022-06-07
 */
@Slf4j
@Service
public class ImageInfoServiceImpl extends ServiceImpl<ImageInfoServiceMapper, ImageInfo> implements ImageInfoService {

    /**
     * 本地图片存放路径
     */
    private static String UPLOAD_PATH = "File/Image";

    /**
     * ftp服务器ip地址
     */
    @Value("${ftp.address}")
    private String ftpAddress;

    /**
     * 端口号
     */
    @Value("${ftp.port}")
    private int ftpPort;

    /**
     * 用户名
     */
    @Value("${ftp.username}")
    private String ftpUsername;

    /**
     * 密码
     */
    @Value("${ftp.password}")
    private String ftpPassword;

    /**
     * 图片路径
     */
    @Value("${ftp.basepath}")
    public String ftpBasePath;

    /**
     * 访问路径
     */
    @Value("${ftp.prefix}")
    private String pathPrefix;

    /**
     * 图片下载次数
     */
    @Value("${ftp.image.download.times}")
    private Integer imageDownloadTimes;

    @Autowired
    private ImageInfoServiceMapper imageInfoServiceMapper;

    @Autowired
    private ImageInfoService imageInfoService;

    @Autowired
    private UserService userService;


    /**
     * 单个图片上传
     * @param image 图片请求
     * @param authorizedUsers 授权用户
     * @return Response
     */
    @Override
    public Response saveImage(MultipartFile image, List<String> authorizedUsers) {
        // 获取当前登录用户名
        UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String username = principal.getUsername();

        // 检查输入的authorizedUsers是否存在
        String notExistString = userService.isAuthorizedUsersExist(authorizedUsers);
        if (notExistString.length() > 0) {
            return ResponseGenerator.genFailResponse("输入的授权用户 " + notExistString + " 无效,请重新检查!");
        }

        List<String> paths = new ArrayList<>();
        String path = "";
        try {
            log.info("用户: " + username + " 发起图片上传: " + image.getOriginalFilename());
            // 连接服务器
            JschUtil.connect(ftpAddress, ftpPort, ftpUsername, ftpPassword, ftpBasePath);
            // 重名文件检查与过滤
            String fileName = JschUtil.filterSameNameFile(image.getOriginalFilename());
            // 上传,返回图片路径
            path = JschUtil.ftpUpload(image, pathPrefix, fileName);
            paths.add(path);
            // 断开服务器
            JschUtil.disConnect();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResponseGenerator.genFailResponse("网络错误!当前服务不可用,请尝试重试或联系客服");
        }

        // 图片路径加密
        String encryptPath = EncrUtil.encrypt(path);
        // 图片信息存储
        ImageInfo imageInfo = new ImageInfo();
        imageInfo.setId(0L);
        imageInfo.setImageAddress(encryptPath);
        imageInfo.setImageOwner(username);
        imageInfo.setAllowDownloadTimes(imageDownloadTimes);
        // 若authorizedUsers非空,说明指定了授权用户,自动加入当前登录用户,并去重
        if (!authorizedUsers.isEmpty()) {
            authorizedUsers.add(username);
            authorizedUsers = authorizedUsers.stream().distinct().collect(Collectors.toList());
        }
        imageInfo.setAuthorizedUser(authorizedUsers);
        imageInfo.setIsDeleted(IsDeletedEnum.NO.getCode());
        imageInfo.setCreateTime(new Timestamp(System.currentTimeMillis()));
        imageInfo.setCreateUser(username);
        imageInfo.setUpdateUser(username);

        int insert = imageInfoServiceMapper.insert(imageInfo);
        if (insert > 0) {
            log.info("图片信息上传成功");
            Response response = ResponseGenerator.genSuccessResponse(paths);
            response.setMessage(response.getMessage() + " 上传成功!");
            return response;
        }

        return ResponseGenerator.genFailResponse("网络错误!当前服务不可用,请尝试重试或联系客服");
    }


    /**
     * 单个/多个图片上传
     * @param httpServletRequest 图片请求
     * @param authorizedUsers 授权用户
     * @return Response
     */
    @Override
    public Response saveImages(HttpServletRequest httpServletRequest, List<String> authorizedUsers) {
        // 获取当前登录用户名
        UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String username = principal.getUsername();

        // 检查输入的authorizedUsers是否存在
        String notExistString = userService.isAuthorizedUsersExist(authorizedUsers);
        if (notExistString.length() > 0) {
            return ResponseGenerator.genFailResponse("输入的授权用户 " + notExistString + " 无效,请重新检查!");
        }

        // 提取图片序列
        MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) httpServletRequest;
        Iterator<String> fileNames = multiRequest.getFileNames();
        List<MultipartFile> multipartFiles = multiRequest.getFiles(fileNames.next());

        List<String> paths = new ArrayList<>();
        try {
            log.info("用户: " + username + " 发起图片上传");
            // 连接服务器
            JschUtil.connect(ftpAddress, ftpPort, ftpUsername, ftpPassword, ftpBasePath);
            // 循环上传
            for (MultipartFile image : multipartFiles) {
                String path = "";
                // 重名文件检查与过滤
                String fileName = JschUtil.filterSameNameFile(image.getOriginalFilename());
                // 上传
                path = JschUtil.ftpUpload(image, pathPrefix, fileName);
                paths.add(path);
            }
            // 断开服务器
            JschUtil.disConnect();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResponseGenerator.genFailResponse("网络错误!当前服务不可用,请尝试重试或联系客服");
        }

        // 若authorizedUsers非空,说明指定了授权用户,自动加入当前登录用户,并去重
        if (!authorizedUsers.isEmpty()) {
            authorizedUsers.add(username);
            authorizedUsers = authorizedUsers.stream().distinct().collect(Collectors.toList());
        }
        List<String> finalAuthorizedUsers = authorizedUsers;
        // 构造 entityList,批量插入
        List<ImageInfo> entityList = paths.stream().map(path -> {
            ImageInfo imageInfo = new ImageInfo();
            imageInfo.setImageAddress(EncrUtil.encrypt(path));
            imageInfo.setImageOwner(username);
            imageInfo.setAllowDownloadTimes(imageDownloadTimes);
            imageInfo.setAuthorizedUser(finalAuthorizedUsers);
            imageInfo.setIsDeleted(IsDeletedEnum.NO.getCode());
            imageInfo.setCreateTime(new Timestamp(System.currentTimeMillis()));
            imageInfo.setCreateUser(username);
            imageInfo.setUpdateUser(username);
            return imageInfo;
        }).collect(Collectors.toList());

        boolean insert = imageInfoService.saveBatch(entityList);
        if (insert) {
            log.info("图片信息上传成功");
            Response response = ResponseGenerator.genSuccessResponse(paths);
            response.setMessage(response.getMessage() + " 上传成功!");
            return response;
        }

        return ResponseGenerator.genFailResponse("网络错误!当前服务不可用,请尝试重试或联系客服");
    }


    /**
     * 图片分页查询
     * @param pageRequest 输入页码和页大小
     * @return Response(PageResponse)
     */
    @Override
    public Response imagesPagingPreview(PageRequest pageRequest) {
        // 获取当前登录用户名
        UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String username = principal.getUsername();

        // 筛选username有预览权限的entity,并解码图片地址
        List<ImageInfo> imageInfos = imageInfoServiceMapper.selectList(Wrappers.lambdaQuery(ImageInfo.class)
                .eq(ImageInfo::getIsDeleted, IsDeletedEnum.NO.getCode())
        );
        List<ImageInfo> imageInfoList = imageInfos.stream()
                .filter(new Predicate<ImageInfo>() {
                    // 筛选出权限用户
                    @Override
                    public boolean test(ImageInfo imageInfo) {
                        String[] stringAuthorizedUsers = (String[]) imageInfo.getAuthorizedUser();
                        if (stringAuthorizedUsers == null) {
                            return Boolean.TRUE;
                        }
                        List<String> authorizedUsers = Arrays.asList(stringAuthorizedUsers);
                        return authorizedUsers.isEmpty() || authorizedUsers.contains(username);
                    }
                })
                .map(imageInfo -> {
                    // 图片地址解码
                    imageInfo.setImageAddress(EncrUtil.decrypt(imageInfo.getImageAddress()));
                    return imageInfo;
                }).collect(Collectors.toList());

        PageResponse pageResponse = new PageResponse(imageInfoList, imageInfoList.size(), pageRequest.getPageSize(), pageRequest.getPageNo());
        return ResponseGenerator.genSuccessResponse(pageResponse);
    }


    /**
     * 更新图片信息(预览和下载权限人)
     * @param imageAddress 图片地址
     * @param authorizedUsers 授权用户
     * @return Response
     */
    @Override
    public Response updateImageInfo(String imageAddress, List<String> authorizedUsers) {
        // 获取当前登录用户名
        UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String username = principal.getUsername();

        // 检查输入的authorizedUsers是否存在
        String notExistString = userService.isAuthorizedUsersExist(authorizedUsers);
        if (notExistString.length() > 0) {
            return ResponseGenerator.genFailResponse("输入的授权用户 " + notExistString + " 无效,请重新检查!");
        }

        // 查询需要update的imageinfo
        List<ImageInfo> imageInfos = imageInfoServiceMapper.selectList(Wrappers.lambdaQuery(ImageInfo.class)
                .likeLeft(ImageInfo::getImageOwner, username)
                .likeRight(ImageInfo::getImageOwner, username)
                .likeLeft(ImageInfo::getImageAddress, EncrUtil.encrypt(imageAddress))
                .likeRight(ImageInfo::getImageAddress, EncrUtil.encrypt(imageAddress))
                .eq(ImageInfo::getIsDeleted, IsDeletedEnum.NO.getCode())
        );

        try {
            // 如果没get到,则抛出异常
            ImageInfo imageInfo = imageInfos.get(0);
            // 若authorizedUsers非空,说明指定了授权用户,自动加入当前登录用户,并去重
            if (!authorizedUsers.isEmpty()) {
                authorizedUsers.add(username);
                authorizedUsers = authorizedUsers.stream().distinct().collect(Collectors.toList());
            }
            imageInfo.setAuthorizedUser(authorizedUsers);
            imageInfo.setUpdateTime(new Timestamp(System.currentTimeMillis()));
            boolean update = imageInfoService.updateById(imageInfo);
            if (update) {
                log.info("用户: " + username + " 修改图片信息 " + imageAddress + " 修改成功");
                return ResponseGenerator.genSuccessResponse(authorizedUsers);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        return ResponseGenerator.genFailResponse("网络错误!当前服务不可用,请尝试重试或联系客服");
    }


    /**
     * 单个图片下载
     * @param imageAddress 图片在服务器地址(分页查询可得)
     * @param downloadPath 下载的本地地址
     * @return Response
     */
    @Override
    public Response downloadImage(String imageAddress, String downloadPath) {
        // 获取当前登录用户名
        UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String username = principal.getUsername();
        log.info("用户: " + username + " 发起图片下载: " + imageAddress);

        // 可下载图片过滤,下载次数限制检查
        List<ImageInfo> imageInfos = imageInfoServiceMapper.selectList(Wrappers.lambdaQuery(ImageInfo.class)
                .likeLeft(ImageInfo::getImageAddress, EncrUtil.encrypt(imageAddress))
                .likeRight(ImageInfo::getImageAddress, EncrUtil.encrypt(imageAddress))
                .eq(ImageInfo::getIsDeleted, IsDeletedEnum.NO.getCode())
                .gt(ImageInfo::getAllowDownloadTimes, 0)
        );
        // 如果imageInfos则为空,则图片已删除,或已经超过下载次数,直接返回!
        if (imageInfos.isEmpty()) {
            log.info("图片已被删除或超过规定下载次数!");
            return ResponseGenerator.genFailResponse("图片已被删除或超过规定下载次数!");
        }
        // 下载授权人过滤
        List<ImageInfo> imageInfoList = imageInfos.stream()
                .filter(new Predicate<ImageInfo>() {
                    // 筛选出权限用户
                    @Override
                    public boolean test(ImageInfo imageInfo) {
                        String[] stringAuthorizedUsers = (String[]) imageInfo.getAuthorizedUser();
                        if (stringAuthorizedUsers == null) {
                            return Boolean.TRUE;
                        }
                        List<String> authorizedUsers = Arrays.asList(stringAuthorizedUsers);
                        return authorizedUsers.isEmpty() || authorizedUsers.contains(username);
                    }
                }).collect(Collectors.toList());
        // 如果过滤掉了,imageInfoList则为空,则用户没有预览和下载权限,直接返回!
        if (imageInfoList.isEmpty()) {
            log.info("用户: " + username + " 没有预览和下载权限!");
            return ResponseGenerator.genFailResponse("用户没有预览和下载权限!");
        }

        // 提取图片名
        String fileName = imageAddress.replaceAll(pathPrefix, "");
        // 判断是否成功的boolean值
        Boolean download = false;
        try {
            // 连接服务器
            JschUtil.connect(ftpAddress, ftpPort, ftpUsername, ftpPassword, ftpBasePath);
            // 下载图片到指定路径
            download = JschUtil.ftpDownload(fileName, downloadPath);
            // 断开服务器
            JschUtil.disConnect();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        // 下载成功
        if (download) {
            // 修改下载次数: 下载次数 - 1
            try {
                ImageInfo imageInfo = imageInfoList.get(0);
                imageInfo.setAllowDownloadTimes(imageInfo.getAllowDownloadTimes() - 1);
                imageInfo.setUpdateTime(new Timestamp(System.currentTimeMillis()));
                boolean update = imageInfoService.updateById(imageInfo);
                if (update) {
                    log.info("用户: " + username + " 下载图片 " + fileName + " 一次");
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }

            List<String> list = new ArrayList<>();
            list.add(downloadPath + fileName);
            Response response = ResponseGenerator.genSuccessResponse(list);
            response.setMessage("下载成功!");
            return response;
        }

        return ResponseGenerator.genFailResponse("网络错误!当前服务不可用,请尝试重试或联系客服");
    }

}

参考资料

blog.csdn.net/weixin_4389…