|
@@ -0,0 +1,134 @@
|
|
|
|
|
+/**
|
|
|
|
|
+ * Cordova 下载适配器
|
|
|
|
|
+ * 使用 cordova-plugin-file 保存文件到本地文件系统
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+import { IDownloadAdapter } from '../IDownloadAdapter';
|
|
|
|
|
+import { DownloadOptions, DownloadResult, DownloadFeatures } from '../types';
|
|
|
|
|
+
|
|
|
|
|
+export class CordovaDownloadAdapter implements IDownloadAdapter {
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 执行下载操作
|
|
|
|
|
+ */
|
|
|
|
|
+ async download(options: DownloadOptions): Promise<DownloadResult> {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const { data, filename } = options;
|
|
|
|
|
+
|
|
|
|
|
+ // 检查 Cordova 环境
|
|
|
|
|
+ if (!window.cordova) {
|
|
|
|
|
+ throw new Error('Cordova 环境不可用');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 将数据转换为 Blob
|
|
|
|
|
+ const blob =
|
|
|
|
|
+ data instanceof Blob
|
|
|
|
|
+ ? data
|
|
|
|
|
+ : new Blob([data], { type: options.mimeType || 'application/octet-stream' });
|
|
|
|
|
+
|
|
|
|
|
+ // 使用 FileReader 读取 Blob 数据
|
|
|
|
|
+ const arrayBuffer = await this.blobToArrayBuffer(blob);
|
|
|
|
|
+
|
|
|
|
|
+ // 保存文件
|
|
|
|
|
+ const filePath = await this.saveFile(filename, arrayBuffer);
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ success: true,
|
|
|
|
|
+ filePath: filePath,
|
|
|
|
|
+ };
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('[Cordova下载] 下载失败:', error);
|
|
|
|
|
+ return {
|
|
|
|
|
+ success: false,
|
|
|
|
|
+ error: error instanceof Error ? error.message : '下载失败',
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将 Blob 转换为 ArrayBuffer
|
|
|
|
|
+ */
|
|
|
|
|
+ private blobToArrayBuffer(blob: Blob): Promise<ArrayBuffer> {
|
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
|
+ const reader = new FileReader();
|
|
|
|
|
+ reader.onload = () => resolve(reader.result as ArrayBuffer);
|
|
|
|
|
+ reader.onerror = reject;
|
|
|
|
|
+ reader.readAsArrayBuffer(blob);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 保存文件到 Cordova 文件系统
|
|
|
|
|
+ */
|
|
|
|
|
+ private saveFile(filename: string, data: ArrayBuffer): Promise<string> {
|
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
|
+ // 检查文件系统 API 是否可用
|
|
|
|
|
+ if (!window.resolveLocalFileSystemURL) {
|
|
|
|
|
+ reject(new Error('Cordova 文件系统 API 不可用'));
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取文件系统
|
|
|
|
|
+ window.resolveLocalFileSystemURL(
|
|
|
|
|
+ (window as any).cordova.file.externalDataDirectory ||
|
|
|
|
|
+ (window as any).cordova.file.dataDirectory,
|
|
|
|
|
+ (dirEntry: any) => {
|
|
|
|
|
+ // 创建文件
|
|
|
|
|
+ dirEntry.getFile(
|
|
|
|
|
+ filename,
|
|
|
|
|
+ { create: true, exclusive: false },
|
|
|
|
|
+ (fileEntry: any) => {
|
|
|
|
|
+ // 创建文件写入器
|
|
|
|
|
+ fileEntry.createWriter(
|
|
|
|
|
+ (fileWriter: any) => {
|
|
|
|
|
+ fileWriter.onwriteend = () => {
|
|
|
|
|
+ console.log('[Cordova下载] 文件保存成功:', fileEntry.nativeURL);
|
|
|
|
|
+ resolve(fileEntry.nativeURL);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ fileWriter.onerror = (error: any) => {
|
|
|
|
|
+ console.error('[Cordova下载] 文件写入失败:', error);
|
|
|
|
|
+ reject(new Error('文件写入失败'));
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 写入数据
|
|
|
|
|
+ const blob = new Blob([data]);
|
|
|
|
|
+ fileWriter.write(blob);
|
|
|
|
|
+ },
|
|
|
|
|
+ (error: any) => {
|
|
|
|
|
+ console.error('[Cordova下载] 创建文件写入器失败:', error);
|
|
|
|
|
+ reject(new Error('创建文件写入器失败'));
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
|
|
+ },
|
|
|
|
|
+ (error: any) => {
|
|
|
|
|
+ console.error('[Cordova下载] 创建文件失败:', error);
|
|
|
|
|
+ reject(new Error('创建文件失败'));
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
|
|
+ },
|
|
|
|
|
+ (error: any) => {
|
|
|
|
|
+ console.error('[Cordova下载] 获取文件系统失败:', error);
|
|
|
|
|
+ reject(new Error('获取文件系统失败'));
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取支持的功能特性
|
|
|
|
|
+ */
|
|
|
|
|
+ getSupportedFeatures(): DownloadFeatures {
|
|
|
|
|
+ return {
|
|
|
|
|
+ supportsFileDialog: false, // Cordova 直接保存到固定目录
|
|
|
|
|
+ supportsCustomFilename: true, // 支持自定义文件名
|
|
|
|
|
+ supportsFileFilters: false, // 不支持文件类型过滤
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取适配器名称
|
|
|
|
|
+ */
|
|
|
|
|
+ getName(): string {
|
|
|
|
|
+ return 'CordovaDownloadAdapter';
|
|
|
|
|
+ }
|
|
|
|
|
+}
|