Skip to content

Latest commit

 

History

History

springboot-db-mongodb

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

Mongo文件预览方案工具类


分享人:郑家骜


[toc]


Mongo文件预览方案工具类

MongoFileUtil 工具类

package com.zja.utils;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsCriteria;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.util.StringUtils;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * Company: 上海数慧系统技术有限公司
 * Department: 数据中心
 * Date: 2021-07-22 10:20
 * Author: zhengja
 * Email: [email protected]
 * Desc:Mongo 操作文件工具类
 */
@Slf4j
public class MongoFileUtil {

    private GridFsTemplate gridFsTemplate;

    private GridFSBucket gridFSBucket;

    /**
     * 预览前缀:例 http://127.0.0.1:8080/dgp-web/public/file/
     */
    private String baseURL;

    /**
     * 本地存储绝对路径:例 D:\\Temp\\mg
     */
    private String localFileMongoPath;

    /**
     * 路径前缀:mg标识存储mongo文件
     */
    private static final String MONGO_PATH_PREFIX = "mg";

    /**
     * 自定义ID key
     */
    private static final String fileKey = "path";


    //----------------------------------------------bean 需要注入才能使用,此类的所有方法

    /**
     * 注入bean
     * @param gridFsTemplate
     * @param gridFSBucket
     * @param localFilePath  本地存储路径    例: C:\\Temp\\storage
     * @param baseURL        基础URL访问路径 例:http://127.0.0.1:80/public/file
     */
    public MongoFileUtil(GridFsTemplate gridFsTemplate, GridFSBucket gridFSBucket, String localFilePath, String baseURL) {
        checkBeanParams(localFilePath, baseURL);
        this.gridFsTemplate = gridFsTemplate;
        this.gridFSBucket = gridFSBucket;
        this.localFileMongoPath = localFilePath + File.separator + MONGO_PATH_PREFIX;
        this.baseURL = baseURL + "/" + MONGO_PATH_PREFIX;
//        initLocalFileMongoPath();
    }


    //---------------------------------------------所有操作方法

    /**
     * 从本地上传文件到 mongo
     * @param path 自定义ID {@link com.zja.utils.id.IdUtil#mgdbId()}
     * @param file 文件对象
     */
    public void uploadByFile(String path, File file) {
        //注意 path 不能重复,多个相同path只取第一个元素(文件)
        uploadByFile(path, null, file);
    }

    /**
     * 从本地上传文件到 mongo :支持自定义文件名称
     * @param path  自定义ID {@link com.zja.utils.id.IdUtil#mgdbId()}
     * @param newFileName 新的文件名称,mongo中存储的文件名
     * @param file
     */
    public void uploadByFile(String path, String newFileName, File file) {
        if (!file.exists()) {
            log.error("fileAbsolutePath: {}", file.getAbsolutePath());
            throw new RuntimeException("上传的文件file不存在!");
        }
        if (!file.isFile()) {
            log.error("fileAbsolutePath: {}", file.getAbsolutePath());
            throw new RuntimeException("上传的文件file不是文件类型!");
        }
        if (null == newFileName) {
            newFileName = file.getName();
        }

        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
            uploadByStream(path, newFileName, inputStream);
        } catch (FileNotFoundException e) {
            log.error("从本地文件上传到mongo失败,文件绝对路径:{}", file.getAbsolutePath());
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                log.error("文件流关闭失败 {}", file.getAbsoluteFile());
                e.printStackTrace();
            }
        }
    }

    /**
     * 存储文件
     * @param inputStream
     * @param path 自定义ID {@link com.zja.utils.id.IdUtil#mgdbId()}
     * @param fileName 文件名称(带后缀)
     * @return 上传失败,则抛异常
     */
    public void uploadByStream(String path, String fileName, InputStream inputStream) {
        checkPath(path);
        if (null == fileName) {
            throw new RuntimeException("上传的文件fileName不能为空!");
        }

        DBObject metadata = new BasicDBObject(fileKey, path);
        //mongoFileId 返回文件ID 注意,返回的不是自定义path
        String mongoFileId = gridFsTemplate.store(inputStream, fileName, metadata).toString();

        if (mongoFileId == null) {
            throw new RuntimeException("文件上传失败!");
        }
    }

    /**
     * 获取文件预览url地址
     * @param path
     * @return 返回文件URL网址
     */
    public String getFileURL(String path) {
        return getFileURL(path, null);
    }

    /**
     * 获取文件预览url地址,并且重命名
     * @param path
     * @param newFileName 预览文件的新名称
     * @return 返回文件URL网址
     */
    public String getFileURL(String path, String newFileName) {

        GridFSFile fileInfo = getGridFSFile(path);
        if (null == fileInfo) {
            log.error("根据path=[{}]在mongo中没有查询到file文件!", path);
            throw new RuntimeException("在mongo中没有查询到存在的文件!");
        }

        String fileName = fileInfo.getFilename();
        if (null != newFileName) {
            fileName = newFileName;
        }
        if (null == fileName) {
            log.error("未发现文件名称,若无法正常打开文件,请尝试调用 downloadFile(String path, String newFileName) or downloadFile(String path, String localPath, String fileName) 方法.");
            fileName = path;
        }

        File file = new File(localCacheUniquePath(path) + File.separator + fileName);
        if (!file.exists()) {
            downloadFile(path, localCacheUniquePath(path), fileName);
        }

        String fileUrl = baseURL + "/" + localUniqueId(path) + "/" + fileName;
        return fileUrl;
    }

    /**
     * 从mongo中下载文件到本地
     * @param path
     * 注意:需要配置默认地址:MongoFileUtil.localFileMongoPath
     * @return 返回文件保存路径
     */
    public String downloadFile(String path) {
        String fileName = getFileName(path);
        if (null == fileName) {
            log.error("未发现文件名称,若无法正常打开文件,请尝试调用 downloadFile(String path, String newFileName) or downloadFile(String path, String localPath, String fileName) 方法.");
            fileName = path;
        }
        return downloadFile(path, localCacheUniquePath(path), fileName);
    }

    /**
     * 从mongo中下载文件到本地 并且文件重命名
     * @param path
     * @param newFileName 下载文件的新名称 例:test.txt
     * @return 返回文件保存路径
     */
    public String downloadFile(String path, String newFileName) {
        return downloadFile(path, localCacheUniquePath(path), newFileName);
    }

    /**
     * 从mongo中下载文件
     * @param path
     * @param localPath 本地路径 例:C:\\Temp
     * @param fileName  文件名称 例:test.txt
     * @return 返回文件保存路径
     */
    public String downloadFile(String path, String localPath, String fileName) {
        checkPath(path);
        String localFilePath = localPath + File.separator + fileName;
        File localfile = new File(localFilePath);
        if (localfile.exists()) {
            localfile.delete();
        }
        if (!localfile.getParentFile().exists()) {
            localfile.getParentFile().mkdirs();
        }

        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(localfile);
            InputStream inputStream = getInputStream(path);
            IOUtils.copy(inputStream, fileOutputStream);
            log.debug("从mongo下载文件成功,localFilePath: {}", localFilePath);
            return localFilePath;
        } catch (IOException e) {
            log.error("从mongo下载文件失败,path:{} ,localFilePath: {}", path, localFilePath);
            e.printStackTrace();
        } finally {
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    log.error("从mongo下载文件失败,path:{}", path);
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * 从mongo中获取文件流
     * @param path
     * @return InputStream 直接返回mongo文件流对象,使用时注意关闭流
     */
    public InputStream getInputStream(String path) throws IOException {
        GridFSFile gridFSFile = getGridFSFile(path);
        if (gridFSFile == null) {
            log.error("根据path=[{}]在mongo中没有查询到file文件!", path);
            throw new RuntimeException("在mongo中没有查询到存在的文件!");
        }
        GridFsResource gridFsResource = gridFSFileToGridFsResource(gridFSFile);
        return gridFsResource.getInputStream();
    }

    /**
     * 检查mongo中是否存在文件
     * @param path
     * @return true 存在
     */
    public boolean existFile(String path) {
        GridFSFile gridFSFile = getGridFSFile(path);
        if (gridFSFile == null) {
            return false;
        }
        return true;
    }

    /**
     * 获取文件对象信息
     * @param path
     * @return 文件对象
     */
    public GridFSFile getGridFSFile(String path) {
        checkPath(path);
        GridFsCriteria gridFsCriteria = GridFsCriteria.whereMetaData(fileKey);
        gridFsCriteria.is(path);
        Query query = new Query();
        query.addCriteria(gridFsCriteria);
        //注意:path是唯一的,如果存在多个path,只取第一个文件
        return gridFsTemplate.findOne(query);
    }

    /**
     * 获取mogon中存储的文件名称
     * @param path
     * @return 返回文件名称
     */
    public String getFileName(String path) {
        GridFSFile result = getGridFSFile(path);
        if (result == null) {
            log.error("根据path=[{}]在mongo中没有查询到file文件!", path);
            throw new RuntimeException("在mongo中没有查询到存在的文件!");
        }
        if (null == result.getFilename()) {
            log.error("根据path=[{}]在mongo中没有查询到file文件名称!", path);
            return null;
        }
        return result.getFilename();
    }

    /**
     * 删除文件(同时删除本地缓存文件与mongo文件)
     * @param path
     * @return 返回删除结果 true 成功
     */
    public boolean deleteFile(String path) {
        checkPath(path);
        //1、清理本地缓存文件
        cleanLocalCache(path);
        //2、删除mongo中的文件
        GridFsCriteria gridFsCriteria = GridFsCriteria.whereMetaData(fileKey);
        gridFsCriteria.is(path);
        Query query = new Query();
        query.addCriteria(gridFsCriteria);
        try {
            gridFsTemplate.delete(query);
            log.debug("从mongo中删除文件成功!");
            return true;
        } catch (Exception e) {
            log.error("从mongo中删除文件失败,path:{} ,ExceptionMessage:{}", path, e.getMessage());
        }
        return false;
    }

    /**
     * 清理本地所有缓存, 不会删除mongo存储的文件
     * 删除 localFileMongoPath 目录下的所有资源 例 C:\\Temp\\mg
     * @return true 清理成功
     */
    public boolean cleanLocalAllCache() {
        try {
            return deleteLocalDir(localFileMongoPath);
        } catch (Exception e) {
            log.error("清理所有本地缓存失败:{}", e.getMessage());
        }
        return false;
    }

    /**
     * 清理本地缓存文件, 不会删除mongo存储的文件
     * @param path
     * @return true 清理成功
     */
    public boolean cleanLocalCache(String path) {
        try {
            return deleteLocalDir(localCacheUniquePath(path));
        } catch (Exception e) {
            log.error("本地缓存清理失败:{}", e.getMessage());
        }
        return false;
    }


    //------------------------------------------- 内部方法

    /**
     * 转换器
     * @param gridFsFile
     * @return GridFsResource
     */
    private GridFsResource gridFSFileToGridFsResource(GridFSFile gridFsFile) {
        GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFsFile.getObjectId());
        return new GridFsResource(gridFsFile, gridFSDownloadStream);
    }

    /**
     * 本地缓存唯一路径
     * @param path
     * @return 单个文件的本地缓存唯一路径
     */
    private String localCacheUniquePath(String path) {
        String localUniqueId = localUniqueId(path);
        return localFileMongoPath + File.separator + localUniqueId;
    }

    /**
     * 本地缓存存储文件的唯一ID
     * @param path
     * @return 返回 本地缓存存储文件的唯一ID
     */
    private static String localUniqueId(String path) {
        //只保留后6位,避免名称相同的文件会覆盖
        String uniqueId = "";
        if (path.length() > 6) {
            uniqueId = path.substring(path.length() - 6);
        } else {
            uniqueId = path;
        }
        return uniqueId;
    }

    /**
     * 初始化 mongo本地缓存路径
     */
    private void initLocalFileMongoPath() {
        if (!new File(localFileMongoPath).exists()) {
            try {
                Files.createDirectories(Paths.get(localFileMongoPath));
            } catch (IOException e) {
                log.error("初始化mongo本地缓存路径失败:{}", localFileMongoPath);
                e.printStackTrace();
            }
        }
    }

    /**
     * 删除目录(同时级联删除目录下所有资源文件)
     * @param localDirPath 本地目录路径 例:C:\\Temp\\mg
     * @return true 删除成功
     */
    private static boolean deleteLocalDir(String localDirPath) {
        File f = new File(localDirPath);
        if (!f.exists()) {
            return true;
        }
        if (f.isDirectory()) {
            File[] listFiles = f.listFiles();
            for (File fs : listFiles) {
                deleteLocalDir(fs.toString());
            }
        }
        return f.delete();
    }

    /**
     * 校验 pdth
     * @param path
     */
    private static void checkPath(String path) {
        if (StringUtils.isEmpty(path)) {
            throw new RuntimeException("[path] not is null!");
        }
    }

    /**
     * 校验bean 注入的参数
     * @param localFilePath
     * @param baseURL
     */
    private static void checkBeanParams(String localFilePath, String baseURL) {
        if (StringUtils.isEmpty(localFilePath)) {
            throw new RuntimeException("[localFilePath] not is null!");
        }
        if (StringUtils.isEmpty(baseURL)) {
            throw new RuntimeException("[baseURL] not is null!");
        }
    }
}

注入bean

package com.zja.config;

import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.zja.utils.MongoFileUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;

import javax.annotation.Resource;

/**
 * Company: 上海数慧系统技术有限公司
 * Department: 数据中心
 * Date: 2021-07-22 17:45
 * Author: zhengja
 * Email: [email protected]
 * Desc:Mongodb 配置类
 */
@Configuration
public class MongodbConfig {

    @Resource
    private MongoDbFactory mongoDbFactory;

    @Bean
    public GridFSBucket getGridFSBuckets() {
        MongoDatabase db = mongoDbFactory.getDb();
        return GridFSBuckets.create(db);
    }

    @Bean
    public MongoFileUtil mongoFileUtil(GridFsTemplate gridFsTemplate, GridFSBucket gridFSBucket) {
        return new MongoFileUtil(gridFsTemplate, gridFSBucket, ConfigConstants.file.localStoragePath, ConfigConstants.file.proxyBaseURL());
    }
}

注入配置常量

package com.zja.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * Company: 上海数慧系统技术有限公司
 * Department: 数据中心
 * Date: 2021-07-22 14:33
 * Author: zhengja
 * Email: [email protected]
 * Desc:常量配置
 */
public class ConfigConstants {

    @Component
    public static class file {
        //文件代理地址
        public static String proxyAddress;
        //文件代理路径
        public static String proxyPath;
        //文件本地存储路径
        public static String localStoragePath;

        @Value("${file.proxyAddress}")
        public void setProxyAddress(String proxyAddress) {
            if (null == proxyAddress) {
                throw new RuntimeException("必须配置 file.proxyAddress:");
            }
            file.proxyAddress = proxyAddress;
        }

        @Value("${file.proxyPath}")
        public void setProxyPath(String proxyPath) {
            if (null == proxyPath) {
                throw new RuntimeException("必须配置 file.proxyPath:");
            }
            file.proxyPath = proxyPath;
        }

        @Value("${file.localStoragePath}")
        public void setLocalStoragePath(String localStoragePath) {
            if (null == localStoragePath) {
                throw new RuntimeException("必须配置 file.localStoragePath:");
            }
            file.localStoragePath = localStoragePath;
        }

        /**
         * 代理地址全路径
         * @return http://IP:8080/dgp-web/public/file
         */
        public static String proxyBaseURL() {
            return file.proxyAddress + file.proxyPath;
        }
    }

}

springboot配置代理本地资源路径

@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
        @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 自定义静态资源文件目录 映射到本机服务器目录
        registry.addResourceHandler(ConfigConstants.file.proxyPath + "/**").addResourceLocations("file:" + ConfigConstants.file.localStoragePath + "\\");
    }
}

yaml配置文件

# 文件存储、预览
file:
#  proxyAddress: http://127.0.0.1:80     				 # 代理地址 nginx
  proxyAddress: http://127.0.0.1:8080/dgp-review-web     # 代理地址  springboot项目
  proxyPath: /public/file                 				 # 代理路径 映射file.localStoragePath
  localStoragePath: C:\\Temp\\storage      				 # 本地存储路径 支持文件缓存/预览/下载/上传/转换等文件操作
  • proxyAddress 支持任何代理服务器,主要是代理本地存储路径 localStoragePath

测试示例

下面是部分接口测试示例,其它接口可以查看 MongoFileUtil.java 工具类

    @Resource
    private MongoFileUtil mongoFileUtil;

    @Test
    public void test() {

        //上传mongo id
        String mgdbId = IdUtil.mgdbId();
        printlnAnyObj("mgdbId", mgdbId);

        //上传本地文件到mongo
        mongoFileUtil.uploadByFile(mgdbId, new File("D:\\Temp\\image\\3840x2160.jpg"));

        //检测文件是否存在
        boolean existFile = mongoFileUtil.existFile(mgdbId);
        printlnAnyObj("existFile", existFile);

        //获取文件名称
        String fileName = mongoFileUtil.getFileName(mgdbId);
        printlnAnyObj("fileName", fileName);

        //下载文件到本地
        String downloadFile = mongoFileUtil.downloadFile(mgdbId);
        printlnAnyObj("downloadFile", downloadFile);

        //获取文件预览路径
        String fileURL = mongoFileUtil.getFileURL(mgdbId);
        printlnAnyObj("fileURL", fileURL);

        //删除文件
        boolean deleteFile = mongoFileUtil.deleteFile(mgdbId);
        printlnAnyObj("deleteFile", deleteFile);

        //确认文件是否被删除
        boolean existFile2 = mongoFileUtil.existFile(mgdbId);
        printlnAnyObj("existFile2", existFile2);
    }

测试示例输出结果

mgdbId60fc10f74d0ba36979ee7ff7
existFiletrue
fileName3840x2160.jpg
downloadFileD:\\Temp\\storage\mg\ee7ff7\3840x2160.jpg
fileURLhttp://127.0.0.1:8080/springboot-test-mongodb/public/file/mg/ee7ff7/3840x2160.jpg
deleteFiletrue
existFile2false
2021-07-24 21:09:14.659  INFO  : 总耗时:3.370 s


另外提供两种代理方案

nginx代理
    # nginx代本地文件服务器
    location ^~ /public/file/ {
    # alias   实际访问文件路径  D:/Temp/storage/
    alias   D:/Temp/storage/;
    }
springboot配置代理本地资源路径
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
        @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 自定义静态资源文件目录 映射到本机服务器目录
        registry.addResourceHandler(ConfigConstants.file.proxyPath + "/**").addResourceLocations("file:" + ConfigConstants.file.localStoragePath + "\\");
    }
}