|
|
@@ -3,7 +3,7 @@ import CryptoJS from 'crypto-js';
|
|
|
|
|
|
/**
|
|
|
* WPS 三阶段保存接口实现
|
|
|
- *
|
|
|
+ *
|
|
|
* 注意:当前实现包含前端模拟版本,用于开发和测试
|
|
|
* 生产环境应该使用真实的后端接口
|
|
|
*/
|
|
|
@@ -13,56 +13,56 @@ const USE_MOCK = true; // 设置为 false 使用真实后端接口
|
|
|
|
|
|
// 文档信息接口
|
|
|
export interface FileInfo {
|
|
|
- id: string;
|
|
|
- name: string;
|
|
|
- version: number;
|
|
|
- size: number;
|
|
|
- create_time: number;
|
|
|
- modify_time: number;
|
|
|
- creator_id: string;
|
|
|
- modifier_id: string;
|
|
|
+ id: string;
|
|
|
+ name: string;
|
|
|
+ version: number;
|
|
|
+ size: number;
|
|
|
+ create_time: number;
|
|
|
+ modify_time: number;
|
|
|
+ creator_id: string;
|
|
|
+ modifier_id: string;
|
|
|
}
|
|
|
|
|
|
// 准备上传参数
|
|
|
export interface PrepareUploadParams {
|
|
|
- file_id: string;
|
|
|
+ file_id: string;
|
|
|
}
|
|
|
|
|
|
// 准备上传返回
|
|
|
export interface PrepareUploadResponse {
|
|
|
- digest_types: string[];
|
|
|
+ digest_types: string[];
|
|
|
}
|
|
|
|
|
|
// 获取上传地址参数
|
|
|
export interface GetUploadAddressParams {
|
|
|
- file_id: string;
|
|
|
- name: string;
|
|
|
- size: number;
|
|
|
- digest: Record<string, string>;
|
|
|
- is_manual: boolean;
|
|
|
- attachment_size?: number;
|
|
|
- content_type?: string;
|
|
|
+ file_id: string;
|
|
|
+ name: string;
|
|
|
+ size: number;
|
|
|
+ digest: Record<string, string>;
|
|
|
+ is_manual: boolean;
|
|
|
+ attachment_size?: number;
|
|
|
+ content_type?: string;
|
|
|
}
|
|
|
|
|
|
// 获取上传地址返回
|
|
|
export interface GetUploadAddressResponse {
|
|
|
- url: string;
|
|
|
- method: string;
|
|
|
- headers?: Record<string, string>;
|
|
|
- params?: Record<string, string>;
|
|
|
- send_back_params?: Record<string, string>;
|
|
|
+ url: string;
|
|
|
+ method: string;
|
|
|
+ headers?: Record<string, string>;
|
|
|
+ params?: Record<string, string>;
|
|
|
+ send_back_params?: Record<string, string>;
|
|
|
}
|
|
|
|
|
|
// 上传完成参数
|
|
|
export interface UploadCompleteParams {
|
|
|
- file_id: string;
|
|
|
- request: GetUploadAddressParams;
|
|
|
- response: {
|
|
|
- status_code: number;
|
|
|
- headers?: Record<string, string>;
|
|
|
- body?: string; // base64 编码
|
|
|
- };
|
|
|
- send_back_params?: Record<string, string>;
|
|
|
+ file_id: string;
|
|
|
+ request: GetUploadAddressParams;
|
|
|
+ response: {
|
|
|
+ status_code: number;
|
|
|
+ headers?: Record<string, string>;
|
|
|
+ body?: string; // base64 编码
|
|
|
+ };
|
|
|
+ send_back_params?: Record<string, string>;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -70,17 +70,17 @@ export interface UploadCompleteParams {
|
|
|
* 协商摘要算法
|
|
|
*/
|
|
|
export const prepareUpload = (params: PrepareUploadParams): Promise<PrepareUploadResponse> => {
|
|
|
- if (USE_MOCK) {
|
|
|
- // 前端模拟实现
|
|
|
- return Promise.resolve({
|
|
|
- digest_types: ['sha1', 'md5', 'sha256']
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- return request({
|
|
|
- url: `/v3/3rd/files/${params.file_id}/upload/prepare`,
|
|
|
- method: 'get'
|
|
|
+ if (USE_MOCK) {
|
|
|
+ // 前端模拟实现
|
|
|
+ return Promise.resolve({
|
|
|
+ digest_types: ['sha1', 'md5', 'sha256']
|
|
|
});
|
|
|
+ }
|
|
|
+
|
|
|
+ return request({
|
|
|
+ url: `/v3/3rd/files/${params.file_id}/upload/prepare`,
|
|
|
+ method: 'get'
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
@@ -88,32 +88,32 @@ export const prepareUpload = (params: PrepareUploadParams): Promise<PrepareUploa
|
|
|
* 获取文件上传的目标地址
|
|
|
*/
|
|
|
export const getUploadAddress = (params: GetUploadAddressParams): Promise<GetUploadAddressResponse> => {
|
|
|
- if (USE_MOCK) {
|
|
|
- // 前端模拟实现 - 使用 Blob URL
|
|
|
- return Promise.resolve({
|
|
|
- url: 'mock://upload', // 模拟上传地址
|
|
|
- method: 'PUT',
|
|
|
- headers: {},
|
|
|
- params: {},
|
|
|
- send_back_params: {
|
|
|
- file_id: params.file_id,
|
|
|
- timestamp: Date.now().toString()
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- return request({
|
|
|
- url: `/v3/3rd/files/${params.file_id}/upload/address`,
|
|
|
- method: 'post',
|
|
|
- data: {
|
|
|
- name: params.name,
|
|
|
- size: params.size,
|
|
|
- digest: params.digest,
|
|
|
- is_manual: params.is_manual,
|
|
|
- attachment_size: params.attachment_size,
|
|
|
- content_type: params.content_type
|
|
|
- }
|
|
|
+ if (USE_MOCK) {
|
|
|
+ // 前端模拟实现 - 使用 Blob URL
|
|
|
+ return Promise.resolve({
|
|
|
+ url: 'mock://upload', // 模拟上传地址
|
|
|
+ method: 'PUT',
|
|
|
+ headers: {},
|
|
|
+ params: {},
|
|
|
+ send_back_params: {
|
|
|
+ file_id: params.file_id,
|
|
|
+ timestamp: Date.now().toString()
|
|
|
+ }
|
|
|
});
|
|
|
+ }
|
|
|
+
|
|
|
+ return request({
|
|
|
+ url: `/v3/3rd/files/${params.file_id}/upload/address`,
|
|
|
+ method: 'post',
|
|
|
+ data: {
|
|
|
+ name: params.name,
|
|
|
+ size: params.size,
|
|
|
+ digest: params.digest,
|
|
|
+ is_manual: params.is_manual,
|
|
|
+ attachment_size: params.attachment_size,
|
|
|
+ content_type: params.content_type
|
|
|
+ }
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
@@ -121,57 +121,57 @@ export const getUploadAddress = (params: GetUploadAddressParams): Promise<GetUpl
|
|
|
* 通知接入方上传已完成
|
|
|
*/
|
|
|
export const uploadComplete = (params: UploadCompleteParams): Promise<FileInfo> => {
|
|
|
- if (USE_MOCK) {
|
|
|
- // 前端模拟实现 - 使用 localStorage 存储文件信息
|
|
|
- const fileId = params.file_id;
|
|
|
- const currentTime = Math.floor(Date.now() / 1000);
|
|
|
-
|
|
|
- // 从 localStorage 获取或创建文件记录
|
|
|
- const storageKey = `wps_file_${fileId}`;
|
|
|
- let fileRecord: any = null;
|
|
|
-
|
|
|
- try {
|
|
|
- const stored = localStorage.getItem(storageKey);
|
|
|
- if (stored) {
|
|
|
- fileRecord = JSON.parse(stored);
|
|
|
- }
|
|
|
- } catch (err) {
|
|
|
- console.warn('读取文件记录失败:', err);
|
|
|
- }
|
|
|
-
|
|
|
- // 创建或更新文件信息
|
|
|
- const version = fileRecord ? fileRecord.version + 1 : 1;
|
|
|
- const fileInfo: FileInfo = {
|
|
|
- id: fileId,
|
|
|
- name: params.request.name,
|
|
|
- version: version,
|
|
|
- size: params.request.size,
|
|
|
- create_time: fileRecord ? fileRecord.create_time : currentTime,
|
|
|
- modify_time: currentTime,
|
|
|
- creator_id: fileRecord ? fileRecord.creator_id : 'user_' + Date.now(),
|
|
|
- modifier_id: 'user_' + Date.now()
|
|
|
- };
|
|
|
-
|
|
|
- // 保存到 localStorage
|
|
|
- try {
|
|
|
- localStorage.setItem(storageKey, JSON.stringify(fileInfo));
|
|
|
- console.log('[前端模拟] 文件信息已保存到 localStorage:', fileInfo);
|
|
|
- } catch (err) {
|
|
|
- console.error('[前端模拟] 保存文件信息失败:', err);
|
|
|
- }
|
|
|
-
|
|
|
- return Promise.resolve(fileInfo);
|
|
|
+ if (USE_MOCK) {
|
|
|
+ // 前端模拟实现 - 使用 localStorage 存储文件信息
|
|
|
+ const fileId = params.file_id;
|
|
|
+ const currentTime = Math.floor(Date.now() / 1000);
|
|
|
+
|
|
|
+ // 从 localStorage 获取或创建文件记录
|
|
|
+ const storageKey = `wps_file_${fileId}`;
|
|
|
+ let fileRecord: any = null;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const stored = localStorage.getItem(storageKey);
|
|
|
+ if (stored) {
|
|
|
+ fileRecord = JSON.parse(stored);
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ console.warn('读取文件记录失败:', err);
|
|
|
}
|
|
|
|
|
|
- return request({
|
|
|
- url: `/v3/3rd/files/${params.file_id}/upload/complete`,
|
|
|
- method: 'post',
|
|
|
- data: {
|
|
|
- request: params.request,
|
|
|
- response: params.response,
|
|
|
- send_back_params: params.send_back_params
|
|
|
- }
|
|
|
- });
|
|
|
+ // 创建或更新文件信息
|
|
|
+ const version = fileRecord ? fileRecord.version + 1 : 1;
|
|
|
+ const fileInfo: FileInfo = {
|
|
|
+ id: fileId,
|
|
|
+ name: params.request.name,
|
|
|
+ version: version,
|
|
|
+ size: params.request.size,
|
|
|
+ create_time: fileRecord ? fileRecord.create_time : currentTime,
|
|
|
+ modify_time: currentTime,
|
|
|
+ creator_id: fileRecord ? fileRecord.creator_id : 'user_' + Date.now(),
|
|
|
+ modifier_id: 'user_' + Date.now()
|
|
|
+ };
|
|
|
+
|
|
|
+ // 保存到 localStorage
|
|
|
+ try {
|
|
|
+ localStorage.setItem(storageKey, JSON.stringify(fileInfo));
|
|
|
+ console.log('[前端模拟] 文件信息已保存到 localStorage:', fileInfo);
|
|
|
+ } catch (err) {
|
|
|
+ console.error('[前端模拟] 保存文件信息失败:', err);
|
|
|
+ }
|
|
|
+
|
|
|
+ return Promise.resolve(fileInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ return request({
|
|
|
+ url: `/v3/3rd/files/${params.file_id}/upload/complete`,
|
|
|
+ method: 'post',
|
|
|
+ data: {
|
|
|
+ request: params.request,
|
|
|
+ response: params.response,
|
|
|
+ send_back_params: params.send_back_params
|
|
|
+ }
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
@@ -179,38 +179,35 @@ export const uploadComplete = (params: UploadCompleteParams): Promise<FileInfo>
|
|
|
* @param file 文件 Blob 或 ArrayBuffer
|
|
|
* @param algorithm 算法类型 md5/sha1/sha256
|
|
|
*/
|
|
|
-export const calculateDigest = async (
|
|
|
- file: Blob | ArrayBuffer,
|
|
|
- algorithm: 'md5' | 'sha1' | 'sha256'
|
|
|
-): Promise<string> => {
|
|
|
- let arrayBuffer: ArrayBuffer;
|
|
|
-
|
|
|
- if (file instanceof Blob) {
|
|
|
- arrayBuffer = await file.arrayBuffer();
|
|
|
- } else {
|
|
|
- arrayBuffer = file;
|
|
|
- }
|
|
|
-
|
|
|
- // 转换为 WordArray
|
|
|
- const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer as any);
|
|
|
-
|
|
|
- // 计算摘要
|
|
|
- let hash: any;
|
|
|
- switch (algorithm) {
|
|
|
- case 'md5':
|
|
|
- hash = CryptoJS.MD5(wordArray);
|
|
|
- break;
|
|
|
- case 'sha1':
|
|
|
- hash = CryptoJS.SHA1(wordArray);
|
|
|
- break;
|
|
|
- case 'sha256':
|
|
|
- hash = CryptoJS.SHA256(wordArray);
|
|
|
- break;
|
|
|
- default:
|
|
|
- throw new Error(`不支持的算法: ${algorithm}`);
|
|
|
- }
|
|
|
-
|
|
|
- return hash.toString();
|
|
|
+export const calculateDigest = async (file: Blob | ArrayBuffer, algorithm: 'md5' | 'sha1' | 'sha256'): Promise<string> => {
|
|
|
+ let arrayBuffer: ArrayBuffer;
|
|
|
+
|
|
|
+ if (file instanceof Blob) {
|
|
|
+ arrayBuffer = await file.arrayBuffer();
|
|
|
+ } else {
|
|
|
+ arrayBuffer = file;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 转换为 WordArray
|
|
|
+ const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer as any);
|
|
|
+
|
|
|
+ // 计算摘要
|
|
|
+ let hash: any;
|
|
|
+ switch (algorithm) {
|
|
|
+ case 'md5':
|
|
|
+ hash = CryptoJS.MD5(wordArray);
|
|
|
+ break;
|
|
|
+ case 'sha1':
|
|
|
+ hash = CryptoJS.SHA1(wordArray);
|
|
|
+ break;
|
|
|
+ case 'sha256':
|
|
|
+ hash = CryptoJS.SHA256(wordArray);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new Error(`不支持的算法: ${algorithm}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ return hash.toString();
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
@@ -220,227 +217,221 @@ export const calculateDigest = async (
|
|
|
* @param fileBlob 文件内容
|
|
|
* @param isManual 是否手动保存
|
|
|
*/
|
|
|
-export const saveFileThreePhase = async (
|
|
|
- fileId: string,
|
|
|
- fileName: string,
|
|
|
- fileBlob: Blob,
|
|
|
- isManual: boolean = false
|
|
|
-): Promise<FileInfo> => {
|
|
|
+export const saveFileThreePhase = async (fileId: string, fileName: string, fileBlob: Blob, isManual: boolean = false): Promise<FileInfo> => {
|
|
|
+ try {
|
|
|
+ // 第一阶段:准备上传,协商摘要算法
|
|
|
+ console.log('[WPS保存] 第一阶段:准备上传');
|
|
|
+ const prepareResult = await prepareUpload({ file_id: fileId });
|
|
|
+ const digestTypes = prepareResult.digest_types || ['sha1'];
|
|
|
+ console.log('[WPS保存] 支持的摘要算法:', digestTypes);
|
|
|
+
|
|
|
+ // 计算文件摘要
|
|
|
+ console.log('[WPS保存] 计算文件摘要...');
|
|
|
+ const digest: Record<string, string> = {};
|
|
|
+ for (const type of digestTypes) {
|
|
|
+ if (['md5', 'sha1', 'sha256'].includes(type)) {
|
|
|
+ digest[type] = await calculateDigest(fileBlob, type as any);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ console.log('[WPS保存] 文件摘要:', digest);
|
|
|
+
|
|
|
+ // 第二阶段:获取上传地址
|
|
|
+ console.log('[WPS保存] 第二阶段:获取上传地址');
|
|
|
+ const uploadAddressParams: GetUploadAddressParams = {
|
|
|
+ file_id: fileId,
|
|
|
+ name: fileName,
|
|
|
+ size: fileBlob.size,
|
|
|
+ digest: digest,
|
|
|
+ is_manual: isManual,
|
|
|
+ content_type: fileBlob.type
|
|
|
+ };
|
|
|
+
|
|
|
+ const addressResult = await getUploadAddress(uploadAddressParams);
|
|
|
+ console.log('[WPS保存] 上传地址:', addressResult.url);
|
|
|
+
|
|
|
+ // 上传文件到指定地址
|
|
|
+ console.log('[WPS保存] 上传文件...');
|
|
|
+
|
|
|
+ let uploadResponse: Response;
|
|
|
+ if (USE_MOCK && addressResult.url === 'mock://upload') {
|
|
|
+ // 前端模拟 - 不实际上传,直接模拟响应
|
|
|
+ console.log('[前端模拟] 跳过实际上传,使用模拟响应');
|
|
|
+ uploadResponse = new Response(null, {
|
|
|
+ status: 200,
|
|
|
+ statusText: 'OK',
|
|
|
+ headers: new Headers({
|
|
|
+ 'content-type': 'application/json',
|
|
|
+ 'x-mock': 'true'
|
|
|
+ })
|
|
|
+ });
|
|
|
+
|
|
|
+ // 可选:将文件保存到 IndexedDB(用于更持久的存储)
|
|
|
+ try {
|
|
|
+ await saveFileToIndexedDB(fileId, fileBlob);
|
|
|
+ console.log('[前端模拟] 文件已保存到 IndexedDB');
|
|
|
+ } catch (err) {
|
|
|
+ console.warn('[前端模拟] 保存到 IndexedDB 失败:', err);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 真实上传
|
|
|
+ uploadResponse = await fetch(addressResult.url, {
|
|
|
+ method: addressResult.method || 'PUT',
|
|
|
+ headers: addressResult.headers || {},
|
|
|
+ body: fileBlob
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('[WPS保存] 上传响应状态:', uploadResponse.status);
|
|
|
+
|
|
|
+ // 获取响应头
|
|
|
+ const responseHeaders: Record<string, string> = {};
|
|
|
+ uploadResponse.headers.forEach((value, key) => {
|
|
|
+ responseHeaders[key] = value;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 获取响应体(如果有)
|
|
|
+ let responseBody: string | undefined;
|
|
|
try {
|
|
|
- // 第一阶段:准备上传,协商摘要算法
|
|
|
- console.log('[WPS保存] 第一阶段:准备上传');
|
|
|
- const prepareResult = await prepareUpload({ file_id: fileId });
|
|
|
- const digestTypes = prepareResult.digest_types || ['sha1'];
|
|
|
- console.log('[WPS保存] 支持的摘要算法:', digestTypes);
|
|
|
-
|
|
|
- // 计算文件摘要
|
|
|
- console.log('[WPS保存] 计算文件摘要...');
|
|
|
- const digest: Record<string, string> = {};
|
|
|
- for (const type of digestTypes) {
|
|
|
- if (['md5', 'sha1', 'sha256'].includes(type)) {
|
|
|
- digest[type] = await calculateDigest(fileBlob, type as any);
|
|
|
- }
|
|
|
- }
|
|
|
- console.log('[WPS保存] 文件摘要:', digest);
|
|
|
-
|
|
|
- // 第二阶段:获取上传地址
|
|
|
- console.log('[WPS保存] 第二阶段:获取上传地址');
|
|
|
- const uploadAddressParams: GetUploadAddressParams = {
|
|
|
- file_id: fileId,
|
|
|
- name: fileName,
|
|
|
- size: fileBlob.size,
|
|
|
- digest: digest,
|
|
|
- is_manual: isManual,
|
|
|
- content_type: fileBlob.type
|
|
|
- };
|
|
|
-
|
|
|
- const addressResult = await getUploadAddress(uploadAddressParams);
|
|
|
- console.log('[WPS保存] 上传地址:', addressResult.url);
|
|
|
-
|
|
|
- // 上传文件到指定地址
|
|
|
- console.log('[WPS保存] 上传文件...');
|
|
|
-
|
|
|
- let uploadResponse: Response;
|
|
|
- if (USE_MOCK && addressResult.url === 'mock://upload') {
|
|
|
- // 前端模拟 - 不实际上传,直接模拟响应
|
|
|
- console.log('[前端模拟] 跳过实际上传,使用模拟响应');
|
|
|
- uploadResponse = new Response(null, {
|
|
|
- status: 200,
|
|
|
- statusText: 'OK',
|
|
|
- headers: new Headers({
|
|
|
- 'content-type': 'application/json',
|
|
|
- 'x-mock': 'true'
|
|
|
- })
|
|
|
- });
|
|
|
-
|
|
|
- // 可选:将文件保存到 IndexedDB(用于更持久的存储)
|
|
|
- try {
|
|
|
- await saveFileToIndexedDB(fileId, fileBlob);
|
|
|
- console.log('[前端模拟] 文件已保存到 IndexedDB');
|
|
|
- } catch (err) {
|
|
|
- console.warn('[前端模拟] 保存到 IndexedDB 失败:', err);
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 真实上传
|
|
|
- uploadResponse = await fetch(addressResult.url, {
|
|
|
- method: addressResult.method || 'PUT',
|
|
|
- headers: addressResult.headers || {},
|
|
|
- body: fileBlob
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- console.log('[WPS保存] 上传响应状态:', uploadResponse.status);
|
|
|
-
|
|
|
- // 获取响应头
|
|
|
- const responseHeaders: Record<string, string> = {};
|
|
|
- uploadResponse.headers.forEach((value, key) => {
|
|
|
- responseHeaders[key] = value;
|
|
|
- });
|
|
|
-
|
|
|
- // 获取响应体(如果有)
|
|
|
- let responseBody: string | undefined;
|
|
|
- try {
|
|
|
- const bodyText = await uploadResponse.text();
|
|
|
- if (bodyText) {
|
|
|
- responseBody = btoa(bodyText); // base64 编码
|
|
|
- }
|
|
|
- } catch (err) {
|
|
|
- console.warn('[WPS保存] 无法读取响应体:', err);
|
|
|
- }
|
|
|
-
|
|
|
- // 第三阶段:上传完成通知
|
|
|
- console.log('[WPS保存] 第三阶段:上传完成通知');
|
|
|
- const completeParams: UploadCompleteParams = {
|
|
|
- file_id: fileId,
|
|
|
- request: uploadAddressParams,
|
|
|
- response: {
|
|
|
- status_code: uploadResponse.status,
|
|
|
- headers: responseHeaders,
|
|
|
- body: responseBody
|
|
|
- },
|
|
|
- send_back_params: addressResult.send_back_params
|
|
|
- };
|
|
|
-
|
|
|
- const fileInfo = await uploadComplete(completeParams);
|
|
|
- console.log('[WPS保存] 保存完成,文件信息:', fileInfo);
|
|
|
-
|
|
|
- return fileInfo;
|
|
|
- } catch (error) {
|
|
|
- console.error('[WPS保存] 保存失败:', error);
|
|
|
- throw error;
|
|
|
+ const bodyText = await uploadResponse.text();
|
|
|
+ if (bodyText) {
|
|
|
+ responseBody = btoa(bodyText); // base64 编码
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ console.warn('[WPS保存] 无法读取响应体:', err);
|
|
|
}
|
|
|
-};
|
|
|
|
|
|
+ // 第三阶段:上传完成通知
|
|
|
+ console.log('[WPS保存] 第三阶段:上传完成通知');
|
|
|
+ const completeParams: UploadCompleteParams = {
|
|
|
+ file_id: fileId,
|
|
|
+ request: uploadAddressParams,
|
|
|
+ response: {
|
|
|
+ status_code: uploadResponse.status,
|
|
|
+ headers: responseHeaders,
|
|
|
+ body: responseBody
|
|
|
+ },
|
|
|
+ send_back_params: addressResult.send_back_params
|
|
|
+ };
|
|
|
+
|
|
|
+ const fileInfo = await uploadComplete(completeParams);
|
|
|
+ console.log('[WPS保存] 保存完成,文件信息:', fileInfo);
|
|
|
+
|
|
|
+ return fileInfo;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('[WPS保存] 保存失败:', error);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
/**
|
|
|
* 前端模拟:将文件保存到 IndexedDB
|
|
|
* 用于更持久的本地存储
|
|
|
*/
|
|
|
const saveFileToIndexedDB = (fileId: string, fileBlob: Blob): Promise<void> => {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- const dbName = 'WPS_Files';
|
|
|
- const storeName = 'files';
|
|
|
- const request = indexedDB.open(dbName, 1);
|
|
|
-
|
|
|
- request.onerror = () => {
|
|
|
- reject(new Error('无法打开 IndexedDB'));
|
|
|
- };
|
|
|
-
|
|
|
- request.onsuccess = (event) => {
|
|
|
- const db = (event.target as IDBOpenDBRequest).result;
|
|
|
- const transaction = db.transaction([storeName], 'readwrite');
|
|
|
- const store = transaction.objectStore(storeName);
|
|
|
-
|
|
|
- const fileRecord = {
|
|
|
- id: fileId,
|
|
|
- blob: fileBlob,
|
|
|
- timestamp: Date.now()
|
|
|
- };
|
|
|
-
|
|
|
- const putRequest = store.put(fileRecord);
|
|
|
-
|
|
|
- putRequest.onsuccess = () => {
|
|
|
- resolve();
|
|
|
- };
|
|
|
-
|
|
|
- putRequest.onerror = () => {
|
|
|
- reject(new Error('保存文件到 IndexedDB 失败'));
|
|
|
- };
|
|
|
- };
|
|
|
-
|
|
|
- request.onupgradeneeded = (event) => {
|
|
|
- const db = (event.target as IDBOpenDBRequest).result;
|
|
|
- if (!db.objectStoreNames.contains(storeName)) {
|
|
|
- db.createObjectStore(storeName, { keyPath: 'id' });
|
|
|
- }
|
|
|
- };
|
|
|
- });
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const dbName = 'WPS_Files';
|
|
|
+ const storeName = 'files';
|
|
|
+ const request = indexedDB.open(dbName, 1);
|
|
|
+
|
|
|
+ request.onerror = () => {
|
|
|
+ reject(new Error('无法打开 IndexedDB'));
|
|
|
+ };
|
|
|
+
|
|
|
+ request.onsuccess = (event) => {
|
|
|
+ const db = (event.target as IDBOpenDBRequest).result;
|
|
|
+ const transaction = db.transaction([storeName], 'readwrite');
|
|
|
+ const store = transaction.objectStore(storeName);
|
|
|
+
|
|
|
+ const fileRecord = {
|
|
|
+ id: fileId,
|
|
|
+ blob: fileBlob,
|
|
|
+ timestamp: Date.now()
|
|
|
+ };
|
|
|
+
|
|
|
+ const putRequest = store.put(fileRecord);
|
|
|
+
|
|
|
+ putRequest.onsuccess = () => {
|
|
|
+ resolve();
|
|
|
+ };
|
|
|
+
|
|
|
+ putRequest.onerror = () => {
|
|
|
+ reject(new Error('保存文件到 IndexedDB 失败'));
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ request.onupgradeneeded = (event) => {
|
|
|
+ const db = (event.target as IDBOpenDBRequest).result;
|
|
|
+ if (!db.objectStoreNames.contains(storeName)) {
|
|
|
+ db.createObjectStore(storeName, { keyPath: 'id' });
|
|
|
+ }
|
|
|
+ };
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* 前端模拟:从 IndexedDB 读取文件
|
|
|
*/
|
|
|
export const getFileFromIndexedDB = (fileId: string): Promise<Blob | null> => {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- const dbName = 'WPS_Files';
|
|
|
- const storeName = 'files';
|
|
|
- const request = indexedDB.open(dbName, 1);
|
|
|
-
|
|
|
- request.onerror = () => {
|
|
|
- reject(new Error('无法打开 IndexedDB'));
|
|
|
- };
|
|
|
-
|
|
|
- request.onsuccess = (event) => {
|
|
|
- const db = (event.target as IDBOpenDBRequest).result;
|
|
|
- const transaction = db.transaction([storeName], 'readonly');
|
|
|
- const store = transaction.objectStore(storeName);
|
|
|
- const getRequest = store.get(fileId);
|
|
|
-
|
|
|
- getRequest.onsuccess = () => {
|
|
|
- const result = getRequest.result;
|
|
|
- resolve(result ? result.blob : null);
|
|
|
- };
|
|
|
-
|
|
|
- getRequest.onerror = () => {
|
|
|
- reject(new Error('读取文件失败'));
|
|
|
- };
|
|
|
- };
|
|
|
-
|
|
|
- request.onupgradeneeded = (event) => {
|
|
|
- const db = (event.target as IDBOpenDBRequest).result;
|
|
|
- if (!db.objectStoreNames.contains(storeName)) {
|
|
|
- db.createObjectStore(storeName, { keyPath: 'id' });
|
|
|
- }
|
|
|
- };
|
|
|
- });
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const dbName = 'WPS_Files';
|
|
|
+ const storeName = 'files';
|
|
|
+ const request = indexedDB.open(dbName, 1);
|
|
|
+
|
|
|
+ request.onerror = () => {
|
|
|
+ reject(new Error('无法打开 IndexedDB'));
|
|
|
+ };
|
|
|
+
|
|
|
+ request.onsuccess = (event) => {
|
|
|
+ const db = (event.target as IDBOpenDBRequest).result;
|
|
|
+ const transaction = db.transaction([storeName], 'readonly');
|
|
|
+ const store = transaction.objectStore(storeName);
|
|
|
+ const getRequest = store.get(fileId);
|
|
|
+
|
|
|
+ getRequest.onsuccess = () => {
|
|
|
+ const result = getRequest.result;
|
|
|
+ resolve(result ? result.blob : null);
|
|
|
+ };
|
|
|
+
|
|
|
+ getRequest.onerror = () => {
|
|
|
+ reject(new Error('读取文件失败'));
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ request.onupgradeneeded = (event) => {
|
|
|
+ const db = (event.target as IDBOpenDBRequest).result;
|
|
|
+ if (!db.objectStoreNames.contains(storeName)) {
|
|
|
+ db.createObjectStore(storeName, { keyPath: 'id' });
|
|
|
+ }
|
|
|
+ };
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* 前端模拟:清除所有保存的文件
|
|
|
*/
|
|
|
export const clearAllFiles = (): Promise<void> => {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- // 清除 localStorage
|
|
|
- const keys = Object.keys(localStorage);
|
|
|
- keys.forEach(key => {
|
|
|
- if (key.startsWith('wps_file_')) {
|
|
|
- localStorage.removeItem(key);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 清除 IndexedDB
|
|
|
- const dbName = 'WPS_Files';
|
|
|
- const request = indexedDB.deleteDatabase(dbName);
|
|
|
-
|
|
|
- request.onsuccess = () => {
|
|
|
- console.log('[前端模拟] 所有文件已清除');
|
|
|
- resolve();
|
|
|
- };
|
|
|
-
|
|
|
- request.onerror = () => {
|
|
|
- reject(new Error('清除 IndexedDB 失败'));
|
|
|
- };
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ // 清除 localStorage
|
|
|
+ const keys = Object.keys(localStorage);
|
|
|
+ keys.forEach((key) => {
|
|
|
+ if (key.startsWith('wps_file_')) {
|
|
|
+ localStorage.removeItem(key);
|
|
|
+ }
|
|
|
});
|
|
|
+
|
|
|
+ // 清除 IndexedDB
|
|
|
+ const dbName = 'WPS_Files';
|
|
|
+ const request = indexedDB.deleteDatabase(dbName);
|
|
|
+
|
|
|
+ request.onsuccess = () => {
|
|
|
+ console.log('[前端模拟] 所有文件已清除');
|
|
|
+ resolve();
|
|
|
+ };
|
|
|
+
|
|
|
+ request.onerror = () => {
|
|
|
+ reject(new Error('清除 IndexedDB 失败'));
|
|
|
+ };
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
@@ -448,10 +439,10 @@ export const clearAllFiles = (): Promise<void> => {
|
|
|
* @param documentId 文档ID
|
|
|
*/
|
|
|
export const cleanDocumentComments = (documentId: string | number): Promise<any> => {
|
|
|
- return request({
|
|
|
- url: `/wps/callback/v3/3rd/clean/${documentId}`,
|
|
|
- method: 'put'
|
|
|
- });
|
|
|
+ return request({
|
|
|
+ url: `/wps/callback/v3/3rd/clean/${documentId}`,
|
|
|
+ method: 'put'
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
@@ -459,18 +450,18 @@ export const cleanDocumentComments = (documentId: string | number): Promise<any>
|
|
|
* @param ossId OSS文件ID
|
|
|
*/
|
|
|
export interface FileVersion {
|
|
|
- version: number;
|
|
|
- url: string;
|
|
|
- createTime: number;
|
|
|
- updateTime: number;
|
|
|
+ version: number;
|
|
|
+ url: string;
|
|
|
+ createTime: number;
|
|
|
+ updateTime: number;
|
|
|
}
|
|
|
|
|
|
export const getFileVersionList = (ossId: string | number): Promise<{ data: FileVersion[] }> => {
|
|
|
- return request({
|
|
|
- url: '/wps/callback/v3/3rd/files/list',
|
|
|
- method: 'get',
|
|
|
- params: { ossId }
|
|
|
- });
|
|
|
+ return request({
|
|
|
+ url: '/wps/callback/v3/3rd/files/list',
|
|
|
+ method: 'get',
|
|
|
+ params: { ossId }
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
@@ -479,10 +470,10 @@ export const getFileVersionList = (ossId: string | number): Promise<{ data: File
|
|
|
* @returns 返回当前版本号
|
|
|
*/
|
|
|
export const initWpsDocument = (ossId: string | number): Promise<{ data: number }> => {
|
|
|
- return request({
|
|
|
- url: `/wps/callback/v3/3rd/init/${ossId}`,
|
|
|
- method: 'post'
|
|
|
- });
|
|
|
+ return request({
|
|
|
+ url: `/wps/callback/v3/3rd/init/${ossId}`,
|
|
|
+ method: 'post'
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
@@ -490,10 +481,10 @@ export const initWpsDocument = (ossId: string | number): Promise<{ data: number
|
|
|
* @param ossId OSS文件ID
|
|
|
*/
|
|
|
export const cancelWpsDocument = (ossId: string | number): Promise<any> => {
|
|
|
- return request({
|
|
|
- url: `/wps/callback/v3/3rd/cancel/${ossId}`,
|
|
|
- method: 'delete'
|
|
|
- });
|
|
|
+ return request({
|
|
|
+ url: `/wps/callback/v3/3rd/cancel/${ossId}`,
|
|
|
+ method: 'delete'
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
@@ -501,23 +492,23 @@ export const cancelWpsDocument = (ossId: string | number): Promise<any> => {
|
|
|
* @param fileId 文件ID(格式:ossId_version)
|
|
|
*/
|
|
|
export interface FinalFileInfo {
|
|
|
- ossId: number;
|
|
|
- fileName: string;
|
|
|
- originalName: string;
|
|
|
- fileSuffix: string;
|
|
|
- url: string;
|
|
|
- ext1: string;
|
|
|
- createTime: string;
|
|
|
- createBy: number;
|
|
|
- createByName: string;
|
|
|
- service: string;
|
|
|
- updateTime: string;
|
|
|
+ ossId: number;
|
|
|
+ fileName: string;
|
|
|
+ originalName: string;
|
|
|
+ fileSuffix: string;
|
|
|
+ url: string;
|
|
|
+ ext1: string;
|
|
|
+ createTime: string;
|
|
|
+ createBy: number;
|
|
|
+ createByName: string;
|
|
|
+ service: string;
|
|
|
+ updateTime: string;
|
|
|
}
|
|
|
|
|
|
export const getFinalFile = (fileId: string): Promise<{ data: FinalFileInfo }> => {
|
|
|
- return request({
|
|
|
- url: '/wps/callback/v3/3rd/getFinal',
|
|
|
- method: 'get',
|
|
|
- params: { id: fileId }
|
|
|
- });
|
|
|
+ return request({
|
|
|
+ url: '/wps/callback/v3/3rd/getFinal',
|
|
|
+ method: 'get',
|
|
|
+ params: { id: fileId }
|
|
|
+ });
|
|
|
};
|