临时文件存储

在应用部署为多实例的场景下,不能将文件存储在应用系统内部,可以使用如下两种方式实现文件存储

  1. 使用共享存储,共享存储目录为 /storage/data,在其下自定义目录存储,需要注意的是文件多时有性能问题
  2. 使用对象存储,例如使用 minio 存储

下面给出具体示例,对于临时存储,可以按照日期创建目录或 bucket,再按照日期删除目录或 bucket。完整代码在文章的最后

使用共享存储

按照规则“临时文件夹名+日期”创建今天的操作目录(可按照需求添加用户 id 等完善命名规则),每次上传文件时都会检查昨天文件存储目录是否存在,如果存在则清空文件夹并删除,如果不存在直接存储文件到今天临时目录下。示例代码如下:

//定义临时目录路径前缀
private static final String BASE_DIR = "/storage/data/uploaded_files_";

/**
* 递归删除目录及其内容。
*
* @param dirPath 要删除的目录路径
* @throws IOException 如果发生 I/O 错误
*/
private void deleteDirectory(Path dirPath) throws IOException {
    if (Files.exists(dirPath)) {
        Files.walkFileTree(dirPath, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                Files.delete(file);
                System.out.println("已删除文件: " + file);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                Files.delete(dir);
                System.out.println("已删除目录: " + dir);
                return FileVisitResult.CONTINUE;
            }
        });
    } else {
        System.out.println("指定的目录不存在: " + dirPath);
    }
}

/**
    * 上传文件到共享存储
    * @param file
    */
private void uploadTempFile(MultipartFile file) {
    InputStream ins = null;
    try {
        ins = file.getInputStream();
        // 昨天
        LocalDate yesterday = LocalDate.now().minusDays(1);
        Path yesterdayPath = Paths.get(BASE_DIR + yesterday.format(DateTimeFormatter.BASIC_ISO_DATE));
        if (Files.exists(yesterdayPath)) {
            //清空昨天文件存储目录
            this.deleteDirectory(yesterdayPath);
        }

        // 今天
        LocalDate today = LocalDate.now();
        Path todayPath = Paths.get(BASE_DIR + today.format(DateTimeFormatter.BASIC_ISO_DATE));
        if (Files.notExists(todayPath)) {
            Files.createDirectories(todayPath);
        }
        //上传文件到指定目录
        Path targetFilePath = todayPath.resolve(file.getOriginalFilename());
        Files.copy(ins, targetFilePath, StandardCopyOption.REPLACE_EXISTING);

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        IOUtils.closeQuietly(ins);
    }
}

实际运行效果: 图 0

使用对象存储

实现逻辑与上面方法相似,由于需要操作 minio 的批量删除等操作,需要使用 minio 相关原生方法,通过下面代码获取 minio 客户端实例:

import storage.util.MinioClientFactory;

@Autowired
MinioClientFactory minioClientFactory;

//获取当前请求,通过请求获取到minio客户端
HttpServletRequest request = SpringWebUtil.getRequest();
MinioClient minioClient = minioClientFactory.getMinioClient(request);

通过日期规则定义 bucket 名称(可按照需求添加用户 id 等完善命名规则),实现示例如下:

//自定义存储桶bucket命名前缀
private static final String BUCKET_NAME = "tempbucket";

/**
* 上传文件至minio
* @param file 上传文件
*/
private void uploadMinioTempFile(MultipartFile file) {
    InputStream ins = null;
    try {
        HttpServletRequest request = SpringWebUtil.getRequest();
        // 今天
        LocalDate today = LocalDate.now();
        String todayBucket = BUCKET_NAME + today.format(DateTimeFormatter.BASIC_ISO_DATE);
        // 昨天
        LocalDate yesterday = today.minusDays(1);
        String yesterdayBucket = BUCKET_NAME + yesterday.format(DateTimeFormatter.BASIC_ISO_DATE);

        ins = file.getInputStream();
        // 获取minio客户端
        MinioClient minioClient = minioClientFactory.getMinioClient(request);
        // 处理昨天数据
        if (minioClient.bucketExists(yesterdayBucket)) {
            System.out.println("存储桶存在,准备清理...");

            // 删除存储桶中的所有对象
            minioClient.listObjects(yesterdayBucket).forEach(item -> {
                try {
                    minioClient.removeObject(yesterdayBucket, item.get().objectName());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });

            // 删除空的存储桶
            minioClient.removeBucket(yesterdayBucket);
            System.out.println("存储桶已成功删除.");
        } else if (!minioClient.bucketExists(todayBucket)) {
            minioClient.makeBucket(todayBucket);
        }
        // 上传
        minioClient.putObject(todayBucket, file.getOriginalFilename(), ins, file.getSize(), file.getContentType());
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        IOUtils.closeQuietly(ins);
    }
}

/**
* 获取上传文件
* @param storeFileName 上传对象名称
* @param operateType 操作类型,支持download,view
*/
public void getMinioTempFile(String storeFileName, String operateType) {
    try {
        LocalDate today = LocalDate.now();
        String todayBucket = BUCKET_NAME + today.format(DateTimeFormatter.BASIC_ISO_DATE);

        HttpServletRequest request = SpringWebUtil.getRequest();
        HttpServletResponse response = SpringWebUtil.getResponse();

        MinioClient minioClient = minioClientFactory.getMinioClient(request);
        //获取对象属性
        ObjectStat objectStat = minioClient.statObject(todayBucket, storeFileName);
        response.setContentType(objectStat.contentType());
        //获取对象流
        InputStream inputStream = minioClient.getObject(todayBucket, storeFileName);

        if ("download".equals(operateType)) {
            String headerKey = "Content-Disposition";
            String headerValue = "attachment; filename=\"" + URLEncoder.encode(objectStat.name(), "UTF-8") + "\"";
            response.setHeader(headerKey, headerValue);
        }
        OutputStream os = response.getOutputStream();
        // 将文件内容写入响应流
        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            os.write(buffer, 0, bytesRead);
        }
        os.close();
        inputStream.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

通过“企业门户-->资源-->对象存储管理”可进入到 minio 管理页面,查看 minio 上传相关内容 图 2

获取 minio 登录账号信息,使用管理员账号登录控制台,打开 kube 控制面板,切换到“全部命名空间”,在容器组中搜索 minio 关键词,找到 minio-deploy,点击名称进入到详情页,从环境变量中获取登录用户名和密码。

图 4

图 5

登录 minio 查看文件 图 1

完整示例代码

包括两个公共方法

  • 临时文件存储:tempSave
  • 获取 minio 上传文件:getMinioTempFile
package main.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

import io.minio.MinioClient;
import io.minio.ObjectStat;

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

import java.net.URLEncoder;

import com.justep.util.SpringWebUtil;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import main.service.*;
import storage.util.MinioClientFactory;

@Service("main-TestCustomUserService")
public class TestUserServiceCustomImpl implements TestServiceCustom {

    private static final String BASE_DIR = "/storage/data/uploaded_files_";

    //自定义存储桶bucket命名前缀
    private static final String BUCKET_NAME = "tempbucket";

    @Autowired
    MinioClientFactory minioClientFactory;

    public String tempSave(MultipartFile file, String type) throws Exception {
        // 请添加你的业务代码
        if ("file".equals(type)) {
            this.uploadTempFile(file);
        } else {
            this.uploadMinioTempFile(file);
        }
        return "ok";
    }

    /**
     * 递归删除目录及其内容。
     *
     * @param dirPath 要删除的目录路径
     * @throws IOException 如果发生 I/O 错误
     */
    private void deleteDirectory(Path dirPath) throws IOException {
        if (Files.exists(dirPath)) {
            Files.walkFileTree(dirPath, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    Files.delete(file);
                    System.out.println("已删除文件: " + file);
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    Files.delete(dir);
                    System.out.println("已删除目录: " + dir);
                    return FileVisitResult.CONTINUE;
                }
            });
        } else {
            System.out.println("指定的目录不存在: " + dirPath);
        }
    }

    /**
     * 上传文件到共享存储
     * @param file
     */
    private void uploadTempFile(MultipartFile file) {
        InputStream ins = null;
        try {
            ins = file.getInputStream();
            // 昨天
            LocalDate yesterday = LocalDate.now().minusDays(1);
            Path yesterdayPath = Paths.get(BASE_DIR + yesterday.format(DateTimeFormatter.BASIC_ISO_DATE));
            if (Files.exists(yesterdayPath)) {
                //清空昨天文件存储目录
                this.deleteDirectory(yesterdayPath);
            }

            // 今天
            LocalDate today = LocalDate.now();
            Path todayPath = Paths.get(BASE_DIR + today.format(DateTimeFormatter.BASIC_ISO_DATE));
            if (Files.notExists(todayPath)) {
                Files.createDirectories(todayPath);
            }
            //上传文件到指定目录
            Path targetFilePath = todayPath.resolve(file.getOriginalFilename());
            Files.copy(ins, targetFilePath, StandardCopyOption.REPLACE_EXISTING);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(ins);
        }
    }

    /**
     * 上传文件至minio
     * @param file 上传文件
     */
    private void uploadMinioTempFile(MultipartFile file) {
        InputStream ins = null;
        try {
            HttpServletRequest request = SpringWebUtil.getRequest();
            // 今天
            LocalDate today = LocalDate.now();
            String todayBucket = BUCKET_NAME + today.format(DateTimeFormatter.BASIC_ISO_DATE);
            // 昨天
            LocalDate yesterday = today.minusDays(1);
            String yesterdayBucket = BUCKET_NAME + yesterday.format(DateTimeFormatter.BASIC_ISO_DATE);

            ins = file.getInputStream();
            // 获取minio客户端
            MinioClient minioClient = minioClientFactory.getMinioClient(request);
            // 处理昨天数据
            if (minioClient.bucketExists(yesterdayBucket)) {
                System.out.println("存储桶存在,准备清理...");

                // 删除存储桶中的所有对象
                minioClient.listObjects(yesterdayBucket).forEach(item -> {
                    try {
                        minioClient.removeObject(yesterdayBucket, item.get().objectName());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });

                // 删除空的存储桶
                minioClient.removeBucket(yesterdayBucket);
                System.out.println("存储桶已成功删除.");
            } else if (!minioClient.bucketExists(todayBucket)) {
                minioClient.makeBucket(todayBucket);
            }
            // 上传
            minioClient.putObject(todayBucket, file.getOriginalFilename(), ins, file.getSize(), file.getContentType());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(ins);
        }
    }

    /**
     * 获取上传文件
     * @param storeFileName 上传对象名称
     * @param operateType 操作类型,支持download,view
     */
    public void getMinioTempFile(String storeFileName, String operateType) {
        try {
            LocalDate today = LocalDate.now();
            String todayBucket = BUCKET_NAME + today.format(DateTimeFormatter.BASIC_ISO_DATE);

            HttpServletRequest request = SpringWebUtil.getRequest();
            HttpServletResponse response = SpringWebUtil.getResponse();

            MinioClient minioClient = minioClientFactory.getMinioClient(request);

            ObjectStat objectStat = minioClient.statObject(todayBucket, storeFileName);
            response.setContentType(objectStat.contentType());
            InputStream inputStream = minioClient.getObject(todayBucket, storeFileName);

            if ("download".equals(operateType)) {
                String headerKey = "Content-Disposition";
                String headerValue = "attachment; filename=\"" + URLEncoder.encode(objectStat.name(), "UTF-8") + "\"";
                response.setHeader(headerKey, headerValue);
            }
            OutputStream os = response.getOutputStream();
            // 将文件内容写入响应流
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            inputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

results matching ""

    No results matching ""