Minio文件预览(docx格式的word文件转pdf文件预览)

一.  Linux搭建好Minio服务后,springboot项目中配置好minio相关信息,详见主页其它文章;

二.  传文件前的相关Minio基础类信息 & 转换工具类code如下:

  1. Minio相关配置类:

  创建minio工具类接口:

package com.geb.bpgms.sheet.infrastructure.client.oss;

import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;

/**
 * @author songkz
 */
public interface OssClient {
    /**
     * 上传文件,并获取文件的URL链接
     */
    String uploadFile(MultipartFile file, String fileName, String type) throws Exception;

    void  delFile(String fileName) throws Exception;

    InputStream downloadFile(String filename) throws Exception;

    /** 预览文件链接 */
    String presignedObjectUrl(String url) throws Exception;


}

Minio实现类:

package com.geb.bpgms.sheet.infrastructure.client.oss;

import com.geb.bpgms.document.infrastructure.client.config.MinioClientConfig;
import io.minio.*;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.concurrent.TimeUnit;


/**
 * @author songkz
 */
@Service
@Slf4j
public class FinancialMinioOssClient implements OssClient {

    private final MinioClient financialMinioClient;
    private final MinioClient previewPubMinioClient;

    private final MinioClientConfig.MinioProperties minioProperties;


    public FinancialMinioOssClient(MinioClientConfig.MinioProperties minioProperties,
                                   @Qualifier(value = "financialMinioClient") MinioClient financialMinioClient,
                                   @Qualifier(value = "previewPubMinioClient") MinioClient previewPubMinioClient) {
        this.minioProperties = minioProperties;
        this.financialMinioClient = financialMinioClient;
        this.previewPubMinioClient = previewPubMinioClient;
    }

    @Override
    public String uploadFile(MultipartFile file, String uniqueFileName, String type) throws Exception {
        String uniqueFileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
        try (InputStream inputStream = file.getInputStream()) {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .bucket(minioProperties.getFinancialBucketName())
                    .object(uniqueFileName)
                    .stream(inputStream, file.getSize(), -1)
                    .contentType(type)
                    .build();
            financialMinioClient.putObject(putObjectArgs);
            return uniqueFileName;
        } catch (Exception e) {
            log.error("上传minio失败,{}", e.getMessage());
            throw new Exception("Error occurred while uploading file: " + e.getMessage(), e);
        }
    }


    @Override
    public String presignedObjectUrl(String url) throws Exception{
        return  previewPubMinioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET) // 请求方法
                            .bucket(minioProperties.getFinancialBucketName())
                            .object(url)
                            .expiry(1, TimeUnit.DAYS)
                            .build()
        );
    }

    @Override
    public void delFile(String fileName) throws Exception {
        financialMinioClient.removeObject(RemoveObjectArgs.builder().bucket(minioProperties.getRagBucketName()).object(fileName).build());
    }

    @Override
    public InputStream downloadFile(String filename) throws Exception {
        try {
            return financialMinioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(minioProperties.getFinancialBucketName())
                            .object(filename)
                            .build()
            );
        } catch (Exception e) {
            throw new Exception("Error occurred while downloading file: " + e.getMessage(), e);
        }
    }
}

Minio相关配置类:

package com.geb.bpgms.document.infrastructure.client.config;


import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author songkz
 */
@Configuration
@EnableConfigurationProperties(MinioClientConfig.MinioProperties.class)
public class MinioClientConfig {

    @Value("${minio.pubPointUrl}")
    private String pubPointUrl;
    private final MinioProperties properties;

    public MinioClientConfig(MinioProperties properties) {
        this.properties = properties;
    }

    @Bean(name = "ragMinioClient")
    public MinioClient ragMinioClient() {
        MinioClient minioClient = MinioClient.builder()
                .endpoint(properties.getUrl())
                .credentials(properties.getAccessKey(), properties.getSecretKey())
                .build();

        // Check if the bucket already exists, and create it if it doesn't
        try {
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(properties.getRagBucketName()).build());
            if (!isExist) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(properties.getRagBucketName()).build());
            }
        } catch (Exception e) {
            throw new RuntimeException("Error checking or creating bucket: " + e.getMessage(), e);
        }

        return minioClient;
    }

    @Bean(name = "financialMinioClient")
    public MinioClient financialMinioClient() {
        MinioClient minioClient = MinioClient.builder()
                .endpoint(properties.getUrl())
                .credentials(properties.getAccessKey(), properties.getSecretKey())
                .build();

        // Check if the bucket already exists, and create it if it doesn't
        try {
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(properties.getFinancialBucketName()).build());
            if (!isExist) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(properties.getFinancialBucketName()).build());
            }
        } catch (Exception e) {
            throw new RuntimeException("Error checking or creating bucket: " + e.getMessage(), e);
        }

        return minioClient;
    }


    @Bean(name = "previewPubMinioClient")
    public MinioClient previewPubMinioClient() {
        MinioClient minioClient = MinioClient.builder()
                .endpoint(pubPointUrl)
                .credentials(properties.getAccessKey(), properties.getSecretKey())
                .build();

        // Check if the bucket already exists, and create it if it doesn't
        try {
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(properties.getFinancialBucketName()).build());
            if (!isExist) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(properties.getFinancialBucketName()).build());
            }
        } catch (Exception e) {
            throw new RuntimeException("Error checking or creating bucket: " + e.getMessage(), e);
        }

        return minioClient;
    }

    @ConfigurationProperties(prefix = "minio")
    @Getter
    @Setter
    public static class MinioProperties {
        private String url;
        private String accessKey;
        private String secretKey;
        private String ragBucketName;
        private String financialBucketName;

    }
}

Minio配置详细信息:

这里的配置信息我们放到了nacos上,也可直接在yaml中配置

minio:
  url: ${minio.host}
  pubPointUrl: ${minio.pubHost}
  access-key: ${minio.accessKey}
  secret-key: ${minio.secretKey}
  rag-bucket-name: minio-test
  financial-bucket-name: minio-finalcial-test

2. Word转Pdf工具类:

package com.geb.bpgms.util;
import com.aspose.words.Shape;
import com.aspose.words.*;
import org.springframework.core.io.ClassPathResource;

import java.awt.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

//work转pdf工具类
public class WordToPdfUtil {
    private static boolean license = false;

    public static void main(String[] args) {
        WordToPdfUtil wordToPdfUtil = new WordToPdfUtil();
        try {

            String wordFile = "/Users/unifig/work/GEB/code/GEB/wisoss/sql/API信息调研(14).docx";
            String pdfFile = "/Users/unifig/work/GEB/code/GEB/wisoss/sql/e17.pdf";
            wordToPdfUtil.wordToPdf(wordFile,pdfFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 将Word文档转换为PDF格式-无水印
     * 使用Apose库实现文档转换功能,需要先加载License以激活无水印转换。
     *
     * @param wordPath Word文档的路径,包括文件名和扩展名。
     * @param pdfPath 生成的PDF文档的路径,包括文件名和扩展名。
     * @throws Exception 如果转换过程中发生错误,将抛出异常。
     * @return 转换失败时返回null,成功时返回非null值。
     */
    public static String wordToPdf(String wordPath, String pdfPath) throws Exception {
        FileOutputStream os = null;
        try {
            // 从classpath中加载Apose的License文件,以启用无水印转换。
            // 凭证 不然切换后有水印
            InputStream is = new ClassPathResource("/lc.xml").getInputStream();

            License aposeLic = new License();
            aposeLic.setLicense(is);
            license = true;
            // 检查License是否成功加载,如果未成功,则输出错误信息并中止转换。
            if (!license) {
                System.out.println("License验证不通过...");
                return null;
            }
            // 创建一个空的PDF文件,准备写入转换后的内容。
            //生成一个空的PDF文件
            File file = new File(pdfPath);
            os = new FileOutputStream(file);
            // 创建一个Document对象,指定要转换的Word文档路径。
            //要转换的word文件
            Document doc = new Document(wordPath);
            doc.save(os, SaveFormat.PDF);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 确保关闭文件输出流。
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return null;
    }


    /**
     * 将指定文件转换为PDF格式,并添加水印。
     *
     * @param toFilePath 目标文件夹路径,用于保存转换后的PDF文件。
     * @param fileName 原始文件名。
     * @param type 原始文件类型,支持".doc"和".docx"。
     * @return 转换后的PDF文件名,如果类型不支持则返回null。
     * @throws Exception 如果转换过程中发生错误,则抛出异常。
     */
    public static String file2pdf(String toFilePath, String fileName, String type ) throws Exception {
        String htmFileName;
        // 根据原始文件类型确定转换后的PDF文件名。
        //获取转换成PDF之后文件名
        if(".doc".equals(type)){
            htmFileName = fileName+".pdf";
        }else if(".docx".equals(type)){
            htmFileName = fileName+".pdf";
        }else{
            // 如果不支持的文件类型,则返回null。
            return null;
        }
        // 创建转换后的PDF文件对象。
        //通过转换之后的PDF文件名,创建PDF文件
        File htmlOutputFile = new File(toFilePath + File.separatorChar + htmFileName);
        // 获取文件输出流,用于写入转换后的PDF文件。
        //获取文件输出流
        FileOutputStream os = new FileOutputStream(htmlOutputFile);
        // 创建Doc文档对象模型,用于读取原始文档并进行转换。
        //获取Doc文档对象模型
        Document doc = new Document(toFilePath+ File.separatorChar + fileName+type);
        // 为文档添加水印文本。
        //为doc文档添加水印
        insertWatermarkText(doc, "");//这里是水印内容
        // 将文档保存为PDF格式,并写入到输出流中。
        //将doc文旦转换成PDF文件并输出到之前创建好的pdf文件中
        doc.save(os, SaveFormat.PDF);
        // 关闭文件输出流。
        //关闭输出流
        if(os!=null){
            os.close();
        }
        // 返回转换后的PDF文件名。
        return htmFileName;
    }

    /**
     * 为Word文档添加文本水印。
     *
     * @param doc 要添加水印的Word文档对象。
     * @param watermarkText 水印文本内容。
     * @throws Exception 如果操作文档过程中发生错误,则抛出异常。
     */
    private static void insertWatermarkText(Document doc, String watermarkText) throws Exception {
        // 创建一个文本形状对象作为水印
        Shape watermark = new Shape(doc, ShapeType.TEXT_PLAIN_TEXT);
        // 设置水印文本内容
        watermark.getTextPath().setText(watermarkText);
        // 设置水印字体及大小
        watermark.getTextPath().setFontFamily("宋体");
        // 设置水印的宽度和高度
        watermark.setWidth(500);
        watermark.setHeight(100);
        // 设置水印的旋转角度
        watermark.setRotation(-40);
        // 设置水印的颜色
        watermark.getFill().setColor(Color.lightGray);
        watermark.setStrokeColor(Color.lightGray);
        // 设置水印的位置属性
        watermark.setRelativeHorizontalPosition(RelativeHorizontalPosition.PAGE);
        watermark.setRelativeVerticalPosition(RelativeVerticalPosition.PAGE);
        watermark.setWrapType(WrapType.NONE);
        watermark.setVerticalAlignment(VerticalAlignment.CENTER);
        watermark.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 创建一个段落对象,用于包含水印形状
        Paragraph watermarkPara = new Paragraph(doc);
        watermarkPara.appendChild(watermark);
        // 遍历文档中的每个节(section),在每个节的主头、首个头和偶数头中插入水印
        for (Section sect : doc.getSections())
        {
            // 在指定类型的头部插入水印
            insertWatermarkIntoHeader(watermarkPara, sect, HeaderFooterType.HEADER_PRIMARY);
            insertWatermarkIntoHeader(watermarkPara, sect, HeaderFooterType.HEADER_FIRST);
            insertWatermarkIntoHeader(watermarkPara, sect, HeaderFooterType.HEADER_EVEN);
        }
        // 提示水印添加完成
        System.out.println("Watermark Set");
    }


    /**
     * 在指定的页面头部插入水印。
     * <p>
     * 本方法用于向指定的页面头部插入一个水印。如果指定类型的头部不存在,则会创建一个新的头部并添加到文档中。
     * 使用深度克隆来确保水印段落在每个页面头部的独立性。
     *
     * @param watermarkPara 水印段落对象,包含水印的文本和格式信息。
     * @param sect 目标节对象,水印将被插入到该节的头部。
     * @param headerType 指定的头部类型,用于获取或创建相应类型的头部。
     * @throws Exception 如果操作失败,抛出异常。
     */
    private static void insertWatermarkIntoHeader(Paragraph watermarkPara, Section sect, int headerType) throws Exception{
        // 根据指定的头部类型获取现有的头部对象,如果不存在则返回null。
        HeaderFooter header = sect.getHeadersFooters().getByHeaderFooterType(headerType);

        // 如果指定类型的头部不存在,则创建一个新的头部对象,并将其添加到节的头部集合中。
        if (header == null)
        {
            header = new HeaderFooter(sect.getDocument(), headerType);
            sect.getHeadersFooters().add(header);
        }

        // 将水印段落深度克隆,并添加到头部对象中,确保每个页面的水印都是独立的。
        header.appendChild(watermarkPara.deepClone(true));
    }

    public static File wordToPdf(InputStream inputStream, String fileName) {

        String pdfFile = "./"+fileName;
        FileOutputStream os = null;
        try {
            // 从classpath中加载Apose的License文件,以启用无水印转换。
            // 凭证 不然切换后有水印
            InputStream is = new ClassPathResource("/lc.xml").getInputStream();

            License aposeLic = new License();
            aposeLic.setLicense(is);
            license = true;
            // 检查License是否成功加载,如果未成功,则输出错误信息并中止转换。
            if (!license) {
                System.out.println("License验证不通过...");
                return null;
            }
            // 创建一个空的PDF文件,准备写入转换后的内容。
            //生成一个空的PDF文件

            // 创建一个Document对象,指定要转换的Word文档路径。
            //要转换的word文件
            Document doc = new Document(inputStream);
            // 创建PDF保存选项
            doc.save(pdfFile,SaveFormat.PDF);
            File file = new File(pdfFile);
            //os = new FileOutputStream(file);
            return file;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 确保关闭文件输出流。
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return null;
    }
}

3. pom.xml配置

  手动导入aspose的jar包放到项目的resources新建个lib的目录下

        <dependency>
            <groupId>com.aspose</groupId>
            <artifactId>aspose-words</artifactId>
            <version>15.12.0</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/aspose-words-19.5jdk.jar</systemPath>
        </dependency>

  在pom.xml中加上这段  Build的plugins中

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.5.15</version>
                <configuration>
                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
                    <includeSystemScope>true</includeSystemScope>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

4. 上传文件接口--多个文件上传(先上传源文件->docx格式的word文件->再异步转为pdf上传):

package com.geb.bpgms.sheet.controller;

import com.geb.bpgms.common.dto.CommonResponse;
import com.geb.bpgms.sheet.controller.response.FileResponse;
import com.geb.bpgms.sheet.domain.service.MergeTaskDataService;
import com.geb.bpgms.sheet.infrastructure.client.oss.FinancialMinioOssClient;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping("/file")
@RequiredArgsConstructor
@Tag(name = "文件处理模块")
@Slf4j
public class FileController {

    @Autowired
    FinancialMinioOssClient financialMinioOssClient;

    @Autowired
    private MergeTaskDataService mergeTaskDataService;

    @PostMapping("/uploadFiles")
    @Operation(summary = "上传多个文件")
    public CommonResponse<List<FileResponse>> uploadFiles(@RequestParam("files") MultipartFile[] files) throws IOException {
        if (files.length < 3) {
            return CommonResponse.fail("Please select three file to upload.");
        }
        List<FileResponse> fileList = new ArrayList<>();
        FileResponse fileResponse ;
        List<String> fileNameList = new ArrayList<>();
        for (MultipartFile file : files) {
            String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
            String filePath = "";
            try{
                filePath = financialMinioOssClient.uploadFile(file, fileName, file.getContentType());
            }catch (Exception e){
                log.error("文件上传失败, {}", e.getMessage());
            }
            fileResponse = new FileResponse();
            fileResponse.setName(fileName);
            fileResponse.setFilePath(filePath);
            fileList.add(fileResponse);
            fileNameList.add(fileName);
        }
        // docx 文件格式转 pdf --> 用于预览
        mergeTaskDataService.uploadWordToPDF(fileNameList);
        return CommonResponse.success(fileList);
    }


    /**
     * 预览文件
     * @param url
     * @return
     */
    @GetMapping("/{url}")
    public CommonResponse<String> viewPdf(@PathVariable String url) {
        if(url.contains(".docx")){
            url = url.replace(".docx", ".pdf");
        }
        String filePath = null;
        try{
            filePath = financialMinioOssClient.presignedObjectUrl(url);
        }catch (Exception e){
            log.error("文件预览失败, {}", e.getMessage());
        }
        return CommonResponse.success(filePath);
    }


}

异步上传pdf实现类:

package com.geb.bpgms.sheet.domain.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import com.geb.bpgms.sheet.infrastructure.client.oss.FinancialMinioOssClient;
import com.geb.bpgms.util.WordToPdfUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.List;
import java.util.Set;

/**
 * @author songkz
 */
@Service
@Slf4j
public class MergeTaskDataService {

    @Autowired
    FinancialMinioOssClient financialMinioOssClient;


    /**
     * 异步上传pdf
     * @param fileNameList
     */
    @Async
    public void uploadWordToPDF(List<String> fileNameList){
        for (String fileName : fileNameList ) {
            if(fileName.contains(".docx")){
                String newFileName = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf";
                try{
                    // minio上下载docx文件
                    InputStream inputStream = financialMinioOssClient.downloadFile(fileName);
                    File fileTemp = WordToPdfUtil.wordToPdf(inputStream, newFileName);
                    // 创建 MultipartFile 对象
                    assert fileTemp != null;
                    MultipartFile file = new MockMultipartFile(newFileName, newFileName,"application/pdf", new FileInputStream(fileTemp));
                    financialMinioOssClient.uploadFile(file, newFileName, "application/pdf");
                }catch (Exception e){
                    log.error("文件上传失败, {}", e.getMessage());
                }
            }
        }
    }



}

PS:由于Word转为pdf过程需要时间,多个文件上传后可异步转换执行,启动类上记得加上

@EnableAsync注解,以此@Async注解才能生效。

最后PS:如果部署到服务器上传后,pdf预览需要设置下中文语言  启动命令内,请自行搜索下~

有问题可留言,多多交流~

        

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于使用MinIO预览PDF文件,你可以按照以下步骤进行操作: 1. 首先,确保你已经在你的应用程序中成功集成了MinIO对象存储服务。 2. 从MinIO存储桶中获取要预览PDF文件。你可以使用MinIO提供的API或SDK来实现这一步骤。 3. 下载PDF.js库,该库是一个开源的JavaScript库,用于在Web浏览器中渲染PDF文件。 4. 在你的Web应用程序中创建一个HTML页面,并将PDF.js库引入到该页面中。 5. 在HTML页面中,使用JavaScript代码来加载并渲染MinIO存储桶中的PDF文件。你可以使用PDF.js提供的API来实现这一功能。 下面是一个简单的示例代码,可用作参考: ```html <!DOCTYPE html> <html> <head> <title>PDF Preview</title> <style> #pdf-preview { width: 100%; height: 500px; } </style> </head> <body> <div id="pdf-preview"></div> <script src="path/to/pdf.js"></script> <script src="path/to/pdf.worker.js"></script> <script> var pdfUrl = 'https://your-minio-bucket-url.com/your-pdf-file.pdf'; PDFJS.getDocument(pdfUrl).promise.then(function(pdf) { pdf.getPage(1).then(function(page) { var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); var viewport = page.getViewport({scale: 1}); canvas.width = viewport.width; canvas.height = viewport.height; var renderContext = { canvasContext: context, viewport: viewport }; page.render(renderContext).promise.then(function() { document.getElementById('pdf-preview').appendChild(canvas); }); }); }); </script> </body> </html> ``` 注意替换代码中的"your-minio-bucket-url.com"和"your-pdf-file.pdf"为你实际的MinIO存储桶URL和PDF文件路径。 这样,当你在浏览器中打开该HTML页面时,就能够预览MinIO
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值