刘光辉
3 小时以前 06f314545ac3514d7ce28939920866ae8188d31c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package jnpf.fileStorage.dms;
 
import jnpf.base.ActionResult;
import jnpf.base.ActionResultCode;
import jnpf.dmsEntity.preview.model.DmsFilePreviewTicket;
import jnpf.dmsEntity.storage.form.DmsPersistentPdfGenerateForm;
import jnpf.dmsEntity.storage.vo.DmsPersistentPdfGenerateVO;
import jnpf.exception.DataException;
import jnpf.onlyoffice.DmsPreviewPdfApi;
import jnpf.onlyoffice.DmsPreviewPdfRequest;
import jnpf.util.JsonUtil;
import jnpf.util.TicketUtil;
import lombok.RequiredArgsConstructor;
import org.dromara.x.file.storage.core.FileInfo;
import org.dromara.x.file.storage.core.FileStorageService;
import org.dromara.x.file.storage.core.platform.MinioFileStorage;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
 
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.Collections;
import java.util.Locale;
import java.util.Objects;
 
@Service
@RequiredArgsConstructor
public class DmsPersistentPdfStorageService {
    private static final long TICKET_TTL_SECONDS = 300L;
 
    private final FileStorageService fileStorageService;
    private final DmsPreviewPdfApi previewPdfApi;
 
    public DmsPersistentPdfGenerateVO generate(DmsPersistentPdfGenerateForm form) {
        validateSource(form);
        String ticket = TicketUtil.createTicket(JsonUtil.getObjectToString(sourceTicket(form)),
                TICKET_TTL_SECONDS);
        FileInfo stored = null;
        try {
            ActionResult<byte[]> result = previewPdfApi.render(
                    new DmsPreviewPdfRequest(form.getFileVersionId(), ticket));
            if (result == null || !ActionResultCode.SUCCESS.getCode().equals(result.getCode())
                    || result.getData() == null || result.getData().length == 0) {
                throw new DataException(result == null || result.getMsg() == null
                        ? "持久化 PDF 生成失败" : result.getMsg());
            }
            MultipartFile pdf = new GeneratedPdf(form.getFileVersionId() + ".pdf", result.getData());
            stored = fileStorageService.of(pdf)
                    .setPlatform(form.getStoragePlatform())
                    .setPath("dms-derived/pdf/" + form.getFileVersionId() + "/")
                    .setSaveFilename(form.getSourceChecksumSha256() + ".pdf")
                    .setOriginalFilename(pdf.getOriginalFilename())
                    .setHashCalculatorSha256()
                    .upload();
            if (stored == null || stored.getSize() == null || stored.getSize() <= 0
                    || stored.getHashInfo() == null || stored.getHashInfo().getSha256() == null) {
                deleteQuietly(stored);
                throw new DataException("持久化 PDF 保存失败");
            }
            DmsPersistentPdfGenerateVO value = new DmsPersistentPdfGenerateVO();
            value.setObjectKey(empty(stored.getPath()) + stored.getFilename());
            value.setSizeBytes(stored.getSize());
            value.setChecksumSha256(stored.getHashInfo().getSha256().toLowerCase(Locale.ROOT));
            value.setSourceChecksumSha256(form.getSourceChecksumSha256());
            return value;
        } finally {
            TicketUtil.deleteTicket(ticket);
        }
    }
 
    private void validateSource(DmsPersistentPdfGenerateForm form) {
        String extension = extension(form.getFileName());
        if (!"doc".equals(extension) && !"docx".equals(extension)) {
            throw new DataException("仅支持为 Word 文件生成持久化 PDF");
        }
        MinioFileStorage storage = fileStorageService.getFileStorageVerify(form.getStoragePlatform());
        if (!Objects.equals(storage.getBucketName(), form.getBucketName())) {
            throw new DataException("DMS 原文件存储桶与当前配置不匹配");
        }
    }
 
    private DmsFilePreviewTicket sourceTicket(DmsPersistentPdfGenerateForm form) {
        DmsFilePreviewTicket ticket = new DmsFilePreviewTicket();
        ticket.setScope(DmsFilePreviewTicket.SCOPE);
        ticket.setTenantId(form.getTenantId());
        ticket.setUserId("dms-pdf-generator");
        ticket.setFileVersionId(form.getFileVersionId());
        ticket.setFileName(form.getFileName());
        ticket.setMimeType(form.getMimeType());
        ticket.setSizeBytes(form.getSizeBytes());
        ticket.setStoragePlatform(form.getStoragePlatform());
        ticket.setBucketName(form.getBucketName());
        ticket.setBasePath(empty(form.getBasePath()));
        ticket.setObjectPath(form.getObjectPath());
        ticket.setObjectName(form.getObjectName());
        ticket.setWatermarks(Collections.emptyList());
        ticket.setExpiresAt(System.currentTimeMillis() + TICKET_TTL_SECONDS * 1000L);
        return ticket;
    }
 
    private void deleteQuietly(FileInfo stored) {
        if (stored == null) return;
        try { fileStorageService.delete(stored); }
        catch (RuntimeException ignored) { }
    }
 
    private String extension(String fileName) {
        int dot = fileName == null ? -1 : fileName.lastIndexOf('.');
        return dot < 0 ? "" : fileName.substring(dot + 1).toLowerCase(Locale.ROOT);
    }
 
    private String empty(String value) { return value == null ? "" : value; }
 
    private static final class GeneratedPdf implements MultipartFile {
        private final String fileName;
        private final byte[] content;
 
        private GeneratedPdf(String fileName, byte[] content) {
            this.fileName = fileName;
            this.content = content;
        }
 
        @Override public String getName() { return "file"; }
        @Override public String getOriginalFilename() { return fileName; }
        @Override public String getContentType() { return MediaType.APPLICATION_PDF_VALUE; }
        @Override public boolean isEmpty() { return content.length == 0; }
        @Override public long getSize() { return content.length; }
        @Override public byte[] getBytes() { return content.clone(); }
        @Override public InputStream getInputStream() { return new ByteArrayInputStream(content); }
        @Override public void transferTo(File destination) throws IOException {
            Files.write(destination.toPath(), content);
        }
    }
}