刘光辉
7 小时以前 cf11e63b659afcc9792ab3e395f1b45d844431a8
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
import type { DmsPreviewResourceType } from '#/api/x/dms/preview';
import type { DmsWatermarkParameters } from '#/api/x/dms/watermarkParameters';
 
import { useGlobSetting } from '@jnpf/hooks';
import { createImgPreview } from '@jnpf/ui';
 
import { v4 as uuidv4 } from 'uuid';
 
import { createDmsFilePreviewTicket } from '#/api/x/dms/preview';
import { watermarkParameters } from '#/api/x/dms/watermarkParameters';
import { onlineUtils } from '#/utils/jnpf';
 
const IMAGE_EXTENSIONS = new Set(['bmp', 'gif', 'jpeg', 'jpg', 'png', 'webp']);
const IMAGE_MIME_TYPES = new Set(['image/bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/webp', 'image/x-ms-bmp']);
const IMAGE_PREVIEW_OPENING_LOCK_MS = 500;
const ONLY_OFFICE_EXTENSIONS = new Set([
  'csv',
  'doc',
  'docm',
  'docx',
  'dot',
  'dotx',
  'htm',
  'html',
  'odp',
  'ods',
  'odt',
  'otp',
  'ots',
  'ott',
  'pdf',
  'pot',
  'potx',
  'pps',
  'ppsx',
  'ppt',
  'pptm',
  'pptx',
  'rtf',
  'txt',
  'xls',
  'xlsm',
  'xlsx',
  'xlt',
  'xltx',
]);
const openingResources = new Set<string>();
 
export interface OpenDmsFilePreviewOptions extends DmsWatermarkParameters {
  fileName: string;
  mimeType?: string;
  resourceId: string;
  resourceType: DmsPreviewResourceType;
}
 
function fileExtension(fileName: string) {
  const normalized = fileName.trim().toLowerCase();
  return normalized.includes('.') ? normalized.split('.').pop() || '' : '';
}
 
export function supportsImagePreview(fileName: string, mimeType?: string) {
  const normalizedMimeType = mimeType?.split(';')[0]?.trim().toLowerCase() || '';
  return IMAGE_EXTENSIONS.has(fileExtension(fileName)) || IMAGE_MIME_TYPES.has(normalizedMimeType);
}
 
export function supportsOnlyOfficePreview(fileName: string) {
  return ONLY_OFFICE_EXTENSIONS.has(fileExtension(fileName));
}
 
export function dmsPreviewStreamUrl(fileVersionId: string, accessTicket: string) {
  const apiURL = (useGlobSetting().apiURL || '').replace(/\/+$/, '');
  const query = new URLSearchParams({ ticket: accessTicket });
  return `${apiURL}/api/file/Dms/OnlyOffice/${encodeURIComponent(fileVersionId)}?${query.toString()}`;
}
 
export async function openDmsFilePreview(options: OpenDmsFilePreviewOptions) {
  if (!supportsImagePreview(options.fileName, options.mimeType) && !supportsOnlyOfficePreview(options.fileName)) {
    throw new Error('OnlyOffice 不支持该文件格式');
  }
  const openingKey = JSON.stringify([options.resourceType, options.resourceId, watermarkParameters(options)]);
  if (openingResources.has(openingKey)) return false;
  openingResources.add(openingKey);
  let releaseDelay = 0;
  try {
    const response = await createDmsFilePreviewTicket({
      ...watermarkParameters(options),
      operationId: uuidv4(),
      resourceId: options.resourceId,
      resourceType: options.resourceType,
    });
    const ticket = response.data;
    if (!ticket?.accessTicket || !ticket.fileVersionId || !ticket.fileName) {
      throw new Error('后端返回的 DMS 预览票据不完整');
    }
    if (supportsImagePreview(ticket.fileName, ticket.mimeType)) {
      createImgPreview({ imageList: [dmsPreviewStreamUrl(ticket.fileVersionId, ticket.accessTicket)] });
      releaseDelay = IMAGE_PREVIEW_OPENING_LOCK_MS;
      return true;
    }
    if (!supportsOnlyOfficePreview(ticket.fileName)) {
      throw new Error('OnlyOffice 不支持该文件格式');
    }
    onlineUtils.openOfficeDocument({
      dmsAccessTicket: ticket.accessTicket,
      dmsPreviewExpiresAt: ticket.expiresAt,
      refreshDmsPreviewTicket: async () => {
        const refreshed = await createDmsFilePreviewTicket({
          ...watermarkParameters(options),
          operationId: crypto.randomUUID(),
          resourceId: ticket.fileVersionId,
          resourceType: 'FILE_VERSION',
        });
        return refreshed.data;
      },
      file: {
        fileId: ticket.fileVersionId,
        fileSize: ticket.sizeBytes,
        name: ticket.fileName,
      },
      fileType: 'dms',
      mode: 'view',
      title: ticket.fileName,
    });
    return true;
  } finally {
    if (releaseDelay) {
      setTimeout(() => openingResources.delete(openingKey), releaseDelay);
    } else {
      openingResources.delete(openingKey);
    }
  }
}
 
export function dmsPreviewErrorMessage(error: unknown) {
  return error instanceof Error && error.message ? error.message : '文件预览失败';
}