From 81393fea19f53cd9880ae3a6bdd6b1acb7bae7e6 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Sun, 14 Dec 2025 02:37:33 +0100 Subject: [PATCH] fix: saf download --- lib/services/download_storage_service.dart | 44 ++++++++++++---------- lib/services/saf_storage_service.dart | 43 ++++++++++----------- 2 files changed, 46 insertions(+), 41 deletions(-) diff --git a/lib/services/download_storage_service.dart b/lib/services/download_storage_service.dart index 9ce1ea05..ada695e5 100644 --- a/lib/services/download_storage_service.dart +++ b/lib/services/download_storage_service.dart @@ -556,6 +556,7 @@ class DownloadStorageService { /// Copy a file from temp cache to SAF and return the SAF URI /// Returns null if SAF is not available or copy fails + /// Always cleans up temp file regardless of success/failure Future copyToSaf( String tempFilePath, List pathComponents, @@ -566,34 +567,37 @@ class DownloadStorageService { final safService = SafStorageService.instance; - // Create nested directory structure in SAF - final targetDirUri = await safService.createNestedDirectories( - _customDownloadPath!, - pathComponents, - ); + try { + // Create nested directory structure in SAF + final targetDirUri = await safService.createNestedDirectories( + _customDownloadPath!, + pathComponents, + ); - if (targetDirUri == null) { - return null; - } + if (targetDirUri == null) { + return null; + } - // Copy the file to SAF - final safUri = await safService.copyFileToSaf( - tempFilePath, - targetDirUri, - fileName, - mimeType, - ); + // Copy the file to SAF using native copy + final safUri = await safService.copyFileToSaf( + tempFilePath, + targetDirUri, + fileName, + mimeType, + ); - // Delete temp file after successful copy - if (safUri != null) { + return safUri; + } finally { + // Always clean up temp file regardless of success/failure try { - await File(tempFilePath).delete(); + final tempFile = File(tempFilePath); + if (await tempFile.exists()) { + await tempFile.delete(); + } } catch (_) { // Ignore cleanup errors } } - - return safUri; } /// Get the MIME type for a file extension diff --git a/lib/services/saf_storage_service.dart b/lib/services/saf_storage_service.dart index ad7c8606..c61a7039 100644 --- a/lib/services/saf_storage_service.dart +++ b/lib/services/saf_storage_service.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; @@ -138,7 +139,7 @@ class SafStorageService { } } - /// Copy a file from local storage to SAF directory using streaming + /// Copy a file from local storage to SAF directory using native copy /// Returns the SAF URI of the copied file, or null on failure Future copyFileToSaf( String sourceFilePath, @@ -147,6 +148,7 @@ class SafStorageService { String mimeType, ) async { if (!isAvailable) return null; + try { final sourceFile = File(sourceFilePath); if (!await sourceFile.exists()) { @@ -154,27 +156,26 @@ class SafStorageService { return null; } - // Start a write stream session - final writeInfo = await _safStream.startWriteStream( - targetDirectoryUri, - fileName, - mimeType, - ); - final sessionId = writeInfo.session; + // Use pasteLocalFile for native-side copy (no method channel streaming) + // This is much more efficient for large files and avoids hangs + final result = await _safStream + .pasteLocalFile( + sourceFilePath, + targetDirectoryUri, + fileName, + mimeType, + overwrite: true, + ) + .timeout( + const Duration(minutes: 30), + onTimeout: () => throw TimeoutException('SAF copy timed out'), + ); - final stream = sourceFile.openRead(); - - await for (final chunk in stream) { - await _safStream.writeChunk(sessionId, Uint8List.fromList(chunk)); - } - - // End the write stream - await _safStream.endWriteStream(sessionId); - - debugPrint( - 'SAF copyFileToSaf: successfully copied to $targetDirectoryUri/$fileName', - ); - return writeInfo.fileResult.uri.toString(); + debugPrint('SAF copyFileToSaf: successfully copied to ${result.uri}'); + return result.uri.toString(); + } on TimeoutException catch (e) { + debugPrint('SAF copyFileToSaf timeout: $e'); + return null; } catch (e) { debugPrint('SAF copyFileToSaf error: $e'); return null;