From f832b5d1724ec4c89ebfdef1531d26b355dcba81 Mon Sep 17 00:00:00 2001 From: Dani Garcia Date: Sat, 12 Oct 2024 12:44:19 +0200 Subject: [PATCH 1/3] Download into a file directly --- Sources/SMBClient/FileReader.swift | 31 ++++++++++++++++++++++++++++++ Sources/SMBClient/SMBClient.swift | 6 ++++++ 2 files changed, 37 insertions(+) diff --git a/Sources/SMBClient/FileReader.swift b/Sources/SMBClient/FileReader.swift index 4b54c3b..6408b31 100644 --- a/Sources/SMBClient/FileReader.swift +++ b/Sources/SMBClient/FileReader.swift @@ -62,6 +62,37 @@ public class FileReader { return buffer } + public func download(toLocalFile localFile: URL, overwrite: Bool, progressHandler: (_ progress: Double) -> Void) async throws { + let fileProxy = try await fileProxy() + + var offset: UInt64 = 0 + + let fileManger = FileManager.default + let filePath = localFile.path + let fileExists = fileManger.fileExists(atPath: filePath) + guard let fileHandle = FileHandle(forWritingAtPath: filePath) else { + throw URLError(.cannotWriteToFile) + } + if fileExists, !overwrite { + throw CocoaError(.fileWriteFileExists) + } + defer { + fileHandle.closeFile() + } + var response: Read.Response + repeat { + response = try await session.read( + fileId: fileProxy.id, + offset: offset + ) + fileHandle.seekToEndOfFile() + fileHandle.write(response.buffer) + offset += UInt64(response.buffer.count) + let progress = Double(offset) / Double(fileProxy.size) + progressHandler(progress) + } while NTStatus(response.header.status) != .endOfFile && offset < fileProxy.size + } + public func close() async throws { if let createResponse { try await session.close(fileId: createResponse.fileId) diff --git a/Sources/SMBClient/SMBClient.swift b/Sources/SMBClient/SMBClient.swift index bb7cc74..5aeccbc 100644 --- a/Sources/SMBClient/SMBClient.swift +++ b/Sources/SMBClient/SMBClient.swift @@ -125,6 +125,12 @@ public class SMBClient { return data } + public func download(path: String, localPath: URL, overwrite: Bool = false, progressHandler: (_ progress: Double) -> Void = { _ in }) async throws { + let fileReader = fileReader(path: Pathname.normalize(path)) + try await fileReader.download(toLocalFile: localPath, overwrite: overwrite, progressHandler: progressHandler) + try await fileReader.close() + } + public func upload(content: Data, path: String) async throws { try await upload(content: content, path: Pathname.normalize(path), progressHandler: { _ in }) } From 4864b6b5bf63468e4fce8e9e1cb9619481d54a88 Mon Sep 17 00:00:00 2001 From: Dani Garcia Date: Sat, 12 Oct 2024 13:12:06 +0200 Subject: [PATCH 2/3] Add download into file test --- Tests/SMBClientTests/SMBClientTests.swift | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Tests/SMBClientTests/SMBClientTests.swift b/Tests/SMBClientTests/SMBClientTests.swift index 42d8629..2bdd3e0 100644 --- a/Tests/SMBClientTests/SMBClientTests.swift +++ b/Tests/SMBClientTests/SMBClientTests.swift @@ -379,6 +379,28 @@ final class SMBClientTests: XCTestCase { XCTAssertEqual(data, try Data(contentsOf: fixtureURL.appending(component: "\(user.sharePath)/\(path)"))) } + func testDownloadIntoFile() async throws { + let user = bob + let client = SMBClient(host: "localhost", port: 4445) + try await client.login(username: user.username, password: user.password) + try await client.connectShare(user.share) + + let path = "test_files/file_example_JPG_1MB.jpg" + + var progressWasUpdated: Bool = false + let fileManager = FileManager.default + let tempFolder = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) + let destinationFile = tempFolder.appending(path: "downloadefile.jpg", directoryHint: .notDirectory) + try await client.download(path: path, localPath: destinationFile, overwrite: true) { progress in + progressWasUpdated = true + } + + XCTAssertTrue(fileManager.fileExists(atPath: destinationFile.path)) + let data = try Data(contentsOf: destinationFile) + XCTAssertEqual(data, try Data(contentsOf: fixtureURL.appending(component: "\(user.sharePath)/\(path)"))) + XCTAssertTrue(progressWasUpdated) + } + func testRandomRead01() async throws { let user = bob let client = SMBClient(host: "localhost", port: 4445) From 90b58d75d12df5cd43dec0396472f254c400ff3c Mon Sep 17 00:00:00 2001 From: Dani Garcia Date: Sat, 12 Oct 2024 14:08:11 +0200 Subject: [PATCH 3/3] Create an empty file if file didn't exist yet --- Sources/SMBClient/FileReader.swift | 7 ++++++- Tests/SMBClientTests/SMBClientTests.swift | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Sources/SMBClient/FileReader.swift b/Sources/SMBClient/FileReader.swift index 6408b31..25beef2 100644 --- a/Sources/SMBClient/FileReader.swift +++ b/Sources/SMBClient/FileReader.swift @@ -70,9 +70,14 @@ public class FileReader { let fileManger = FileManager.default let filePath = localFile.path let fileExists = fileManger.fileExists(atPath: filePath) + // If file does not exist, create an empty file so we can create a FileHandle + if !fileExists { + try Data().write(to: localFile) + } guard let fileHandle = FileHandle(forWritingAtPath: filePath) else { - throw URLError(.cannotWriteToFile) + throw URLError(.cannotWriteToFile) } + // If file did already exist and we are not overriding, throw an error if fileExists, !overwrite { throw CocoaError(.fileWriteFileExists) } diff --git a/Tests/SMBClientTests/SMBClientTests.swift b/Tests/SMBClientTests/SMBClientTests.swift index 2bdd3e0..84048b9 100644 --- a/Tests/SMBClientTests/SMBClientTests.swift +++ b/Tests/SMBClientTests/SMBClientTests.swift @@ -390,7 +390,7 @@ final class SMBClientTests: XCTestCase { var progressWasUpdated: Bool = false let fileManager = FileManager.default let tempFolder = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) - let destinationFile = tempFolder.appending(path: "downloadefile.jpg", directoryHint: .notDirectory) + let destinationFile = tempFolder.appending(path: "downloadedfile.jpg", directoryHint: .notDirectory) try await client.download(path: path, localPath: destinationFile, overwrite: true) { progress in progressWasUpdated = true }