@@ -13,18 +13,28 @@ import Foundation
1313/// Buffers are allocated for each new chunk so they can safely escape to other threads
1414/// Call ``close`` when you're done with this object
1515class ChunkedFile {
16-
17- static let SIZE_UNKNOWN : UInt64 = 0
18- /// The size of the file. Call ``open`` to populate this with a real value, otherwise it will be ``SIZE_UNKNOWN``
19- var fileSize : UInt64 {
20- return _fileSize
16+
17+ private struct State {
18+ var fileHandle : FileHandle
19+ var fileURL : URL
20+ var filePosition : UInt64 = 0
2121 }
22-
22+
2323 private let chunkSize : Int
24-
25- private var fileHandle : FileHandle ?
26- private var filePos : UInt64 = 0
27- private var _fileSize : UInt64 = SIZE_UNKNOWN
24+
25+ var fileManager = FileManager . default
26+
27+ private var state : State ?
28+
29+ private var fileHandle : FileHandle ? {
30+ state? . fileHandle
31+ }
32+ private var fileURL : URL ? {
33+ state? . fileURL
34+ }
35+ private var filePos : UInt64 {
36+ state? . filePosition ?? 0
37+ }
2838
2939 /// Reads the next chunk from the file, advancing the file for the next read
3040 /// This method does synchronous I/O, so call it in the background
@@ -39,20 +49,21 @@ class ChunkedFile {
3949 return Result . failure ( ChunkedFileError . fileHandle ( error) )
4050 }
4151 }
42-
52+
4353 /// Opens the internal file ahead of time. Calling this is optional, but it's available
4454 /// Calling this multiple times (on the same thread) will have no effect unless you also ``close`` it
4555 /// Throws if the file couldn't be opened
46- public func openFile( fileURL: URL ) throws {
47- if fileHandle == nil {
56+ func openFile( fileURL: URL ) throws {
57+ if state == nil {
4858 do {
49- guard let fileSize = try FileManager . default . attributesOfItem ( atPath: fileURL. path) [ FileAttributeKey . size] as? UInt64 else {
59+ guard let fileSize = try fileManager . attributesOfItem ( atPath: fileURL. path) [ FileAttributeKey . size] as? UInt64 else {
5060 throw ChunkedFileError . invalidState ( " Cannot retrieve file size " )
5161 }
52- self . _fileSize = fileSize
53-
54- let handle = try FileHandle ( forReadingFrom: fileURL)
55- fileHandle = handle
62+ let fileHandle = try FileHandle ( forReadingFrom: fileURL)
63+ state = State (
64+ fileHandle: fileHandle,
65+ fileURL: fileURL
66+ )
5667 MuxUploadSDK . logger? . info ( " Opened file with len \( String ( describing: fileSize) ) at path \( fileURL. path) " )
5768 } catch {
5869 throw ChunkedFileError . fileHandle ( error)
@@ -68,23 +79,31 @@ class ChunkedFile {
6879 } catch {
6980 MuxUploadSDK . logger? . warning ( " Swallowed error closing file: \( error. localizedDescription) " )
7081 }
71- fileHandle = nil
72- filePos = 0
73- _fileSize = ChunkedFile . SIZE_UNKNOWN
82+ state = nil
7483 }
7584
7685 public func seekTo( byte: UInt64 ) throws {
7786 // Worst case: we start from the begining and there's a few very quick chunk successes
7887 try fileHandle? . seek ( toOffset: byte)
79- filePos = byte
88+ state ? . filePosition = byte
8089 }
8190
8291 private func doReadNextChunk( ) throws -> FileChunk {
8392 MuxUploadSDK . logger? . info ( " --doReadNextChunk " )
84- guard let fileHandle = fileHandle else {
93+ guard let fileHandle = fileHandle, let fileURL = fileURL else {
8594 throw ChunkedFileError . invalidState ( " doReadNextChunk called without file handle. Did you call open()? " )
8695 }
8796 let data = try fileHandle. read ( upToCount: chunkSize)
97+
98+ let fileAttributes = try fileManager. attributesOfItem (
99+ atPath: fileURL. path
100+ )
101+ guard let fileSize = fileAttributes [
102+ FileAttributeKey . size
103+ ] as? UInt64 else {
104+ throw ChunkedFileError . invalidState ( " Cannot retrieve file size " )
105+ }
106+
88107 guard let data = data else {
89108 // Called while already at the end of the file. We read zero bytes, "ending" at the end of the file
90109 return FileChunk ( startByte: fileSize, endByte: fileSize, totalFileSize: fileSize, chunkData: Data ( capacity: 0 ) )
@@ -100,7 +119,7 @@ class ChunkedFile {
100119 chunkData: data
101120 )
102121
103- self . filePos = newFilePos
122+ state ? . filePosition = newFilePos
104123
105124 return chunk
106125 }
0 commit comments