Compare commits

...

2 Commits

Author SHA1 Message Date
Amir Abbas 34c663e62c FileObject.url is unwraped. fixed url initializing from path 2017-04-14 18:57:50 +04:30
Amir Abbas 1415dda987 Fixed #36 (FTP Uploading bug) 2017-04-14 18:55:07 +04:30
11 changed files with 116 additions and 80 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
#
s.name = "FileProvider"
s.version = "0.15.5"
s.version = "0.16.0"
s.summary = "FileManager replacement for Local and Remote (WebDAV/FTP/Dropbox/OneDrive/SMB2) files on iOS and macOS."
# This description is used to generate tags and improve search results.
+2 -2
View File
@@ -621,7 +621,7 @@
799396601D48B7BF00086753 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_VERSION_STRING = 0.15.5;
BUNDLE_VERSION_STRING = 0.16.0;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
@@ -652,7 +652,7 @@
799396611D48B7BF00086753 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_VERSION_STRING = 0.15.5;
BUNDLE_VERSION_STRING = 0.16.0;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
+1 -1
View File
@@ -628,7 +628,7 @@ open class CloudFileProvider: LocalFileProvider {
let path = self.relativePathOf(url: url)
let rpath = path.hasPrefix("/") ? path.substring(from: path.index(after: path.startIndex)) : path
let relativeUrl = URL(string: rpath, relativeTo: self.baseURL)
let relativeUrl = URL(string: rpath.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? rpath, relativeTo: self.baseURL)
let file = FileObject(url: relativeUrl ?? url, name: name, path: path)
file.size = (attribs[NSMetadataItemFSSizeKey] as? NSNumber)?.int64Value ?? -1
+2 -6
View File
@@ -17,19 +17,15 @@ public struct FileProviderDropboxError: FileProviderHTTPError {
/// Containts path, url and attributes of a Dropbox file or resource.
public final class DropboxFileObject: FileObject {
internal init(name: String, path: String) {
super.init(url: URL(string: path) ?? URL(string: "/")!, name: name, path: path)
}
internal convenience init? (jsonStr: String) {
guard let json = jsonStr.deserializeJSON() else { return nil }
self.init(json: json)
}
internal convenience init? (json: [String: AnyObject]) {
internal init? (json: [String: AnyObject]) {
guard let name = json["name"] as? String else { return nil }
guard let path = json["path_display"] as? String else { return nil }
self.init(name: name, path: path)
super.init(url: nil, name: name, path: path)
self.size = (json["size"] as? NSNumber)?.int64Value ?? -1
self.serverTime = Date(rfcString: json["server_modified"] as? String ?? "")
self.modifiedDate = Date(rfcString: json["client_modified"] as? String ?? "")
+8 -6
View File
@@ -442,7 +442,10 @@ extension FTPFileProvider: FileProviderOperations {
return
}
let secondOp = self.copyItem(localFile: localURL, to: destPath, completionHandler: completionHandler) as? RemoteOperationHandle
let secondOp = self.copyItem(localFile: localURL, to: destPath, completionHandler: { error in
completionHandler?(nil)
self.delegateNotify(opType, error: nil)
}) as? RemoteOperationHandle
operationHandle.tasks = secondOp?.tasks ?? []
}) as? RemoteOperationHandle
operationHandle.tasks = firstOp?.tasks ?? []
@@ -475,7 +478,10 @@ extension FTPFileProvider: FileProviderOperations {
return
}
let error = self.throwError(sourcePath, code: URLError.cannotRemoveFile)
var error: Error?
if !response.hasPrefix("2") {
error = self.throwError(sourcePath, code: URLError.cannotRemoveFile)
}
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
@@ -541,7 +547,6 @@ extension FTPFileProvider: FileProviderOperations {
self.ftpStore(task, filePath: self.ftpPath(toPath), fromData: nil, fromFile: localFile, onTask: {
operation.add(task: $0)
$0.taskDescription = opType.json
}, onProgress: { bytesSent, totalSent, expectedBytes in
DispatchQueue.main.async {
self.delegate?.fileproviderProgress(self, operation: opType, progress: Float(Double(totalSent) / Double(expectedBytes)))
@@ -610,7 +615,6 @@ extension FTPFileProvider: FileProviderOperations {
self.ftpRetrieveFile(task, filePath: self.ftpPath(path), onTask: {
operation.add(task: $0)
$0.taskDescription = opType.json
}, onProgress: { recevied, totalReceived, totalSize in
let progress = Double(totalReceived) / Double(totalSize)
self.delegate?.fileproviderProgress(self, operation: opType, progress: Float(progress))
@@ -687,7 +691,6 @@ extension FTPFileProvider: FileProviderReadWrite {
self.ftpRetrieveData(task, filePath: self.ftpPath(path), from: offset, length: length, onTask: {
operation.add(task: $0)
$0.taskDescription = opType.json
}, onProgress: { recevied, totalReceived, totalSize in
let progress = Double(totalReceived) / Double(totalSize)
self.delegate?.fileproviderProgress(self, operation: opType, progress: Float(progress))
@@ -732,7 +735,6 @@ extension FTPFileProvider: FileProviderReadWrite {
let storeHandler = {
self.ftpStore(task, filePath: self.ftpPath(path), fromData: data ?? Data(), fromFile: nil, onTask: {
operation.add(task: $0)
$0.taskDescription = opType.json
}, onProgress: { bytesSent, totalSent, expectedBytes in
DispatchQueue.main.async {
self.delegate?.fileproviderProgress(self, operation: opType, progress: Float(Double(totalSent) / Double(expectedBytes)))
+76 -52
View File
@@ -633,7 +633,70 @@ extension FTPFileProvider {
}
func ftpStore(_ task: FileProviderStreamTask, filePath: String, fromData: Data?, fromFile: URL?, onTask: ((_ task: FileProviderStreamTask) -> Void)?, onProgress: ((_ bytesSent: Int64, _ totalSent: Int64, _ expectedBytes: Int64) -> Void)?, completionHandler: @escaping (_ error: Error?) -> Void) {
let timeout = self.session.configuration.timeoutIntervalForRequest
operation_queue.addOperation {
guard let size: Int64 = (fromData != nil ? Int64(fromData!.count) : nil) ?? fromFile?.fileSize else { return }
var error: Error?
let chunkSize: Int
switch size {
case 0..<262_144: chunkSize = 32_768 // 0KB To 256KB, page size is 32KB
case 262_144..<1_048_576: chunkSize = 65_536 // 256KB To 1MB, page size is 64KB
case 1_048_576..<10_485_760: chunkSize = 131_072 // 1MB To 10MB, page size is 128KB
case 10_048_576..<33_554_432: chunkSize = 262_144 // 1MB To 10MB, page size is 256KB
default: chunkSize = 524_288 // Larger than 32MB, page size is 512KB
}
var fileHandle: FileHandle?
if let file = fromFile {
fileHandle = FileHandle(forReadingAtPath: file.path)
}
defer {
fileHandle?.closeFile()
}
var eof = false
var sent: Int64 = 0
while !eof {
let subdata: Data
if let data = fromData {
let endIndex = min(data.count, Int(sent) + chunkSize)
eof = endIndex == data.count
subdata = data.subdata(in: Int(sent)..<endIndex)
}else if let fileHandle = fileHandle {
subdata = fileHandle.readData(ofLength: chunkSize)
eof = Int64(fileHandle.offsetInFile) == size
} else {
return
}
if subdata.count == 0 { continue }
let group = DispatchGroup()
group.enter()
self.ftpStore(task, data: subdata, to: filePath, from: sent, onTask: onTask, completionHandler: { (serror) in
error = serror
sent += Int64(subdata.count)
group.leave()
onProgress?(Int64(subdata.count), sent, size)
})
let waitResult = group.wait(timeout: .now() + timeout)
if waitResult == .timedOut {
error = self.throwError(filePath, code: URLError.timedOut)
completionHandler(error)
return
}
if let error = error {
completionHandler(error)
return
}
}
completionHandler(nil)
}
}
func ftpStore(_ task: FileProviderStreamTask, data: Data, to filePath: String, from position: Int64, onTask: ((_ task: FileProviderStreamTask) -> Void)?, completionHandler: @escaping (_ error: Error?) -> Void) {
self.ftpDataConnect(task) { (dataTask, error) in
if let error = error {
completionHandler(error)
@@ -646,8 +709,9 @@ extension FTPFileProvider {
}
// Send retreive command
let len = 17 /* */
self.execute(command: "TYPE I" + "\r\n" + "REST 0" + "\r\n" + "STOR \(filePath)", on: task, minLength: 90 + filePath.characters.count, afterSend: { error in
var success = false
let len = 19 /* TYPE response */ + 65 + String(position).characters.count /* REST Response */ + 44 + filePath.characters.count /* STOR open response */ + 10 /* RETR Transfer complete message. */
self.execute(command: "TYPE I" + "\r\n" + "REST \(position)" + "\r\n" + "STOR \(filePath)", on: task, minLength: len, afterSend: { error in
// starting passive task
let timeout = self.session.configuration.timeoutIntervalForRequest
if self.baseURL?.scheme == "ftps" || self.baseURL?.port == 990 {
@@ -655,63 +719,21 @@ extension FTPFileProvider {
}
onTask?(dataTask)
DispatchQueue.global().async {
var error: Error?
let chunkSize = 65536
guard let size: Int64 = (fromData != nil ? Int64(fromData!.count) : nil) ?? fromFile?.fileSize else { return }
var fileHandle: FileHandle?
if let file = fromFile {
fileHandle = FileHandle(forReadingAtPath: file.path)
}
defer {
fileHandle?.closeFile()
}
var eof = false
var sent: Int64 = 0
let group = DispatchGroup()
while !eof {
group.enter()
let subdata: Data
if let data = fromData {
let endIndex = min(data.count, Int(sent) + chunkSize)
eof = endIndex == data.count
subdata = data.subdata(in: Int(sent)..<endIndex)
}else if let fileHandle = fileHandle {
subdata = fileHandle.readData(ofLength: chunkSize)
eof = Int64(fileHandle.offsetInFile) == size
} else {
return
}
if subdata.count == 0 { continue }
dataTask.write(subdata, timeout: timeout, completionHandler: { (serror) in
error = serror
sent += Int64(subdata.count)
onProgress?(Int64(subdata.count), sent, size)
group.leave()
})
}
let waitResult = group.wait(timeout: .now() + timeout)
if data.count == 0 { return }
dataTask.write(data, timeout: timeout, completionHandler: { (error) in
if let error = error {
completionHandler(error)
return
}
if waitResult == .timedOut {
error = self.throwError(fromFile?.relativePath ?? filePath, code: URLError.timedOut)
completionHandler(error)
return
}
success = true
dataTask.closeRead()
dataTask.closeWrite()
completionHandler(nil)
return
}
})
}) { (response, error) in
guard success else { return }
if let error = error {
completionHandler(error)
return
@@ -733,6 +755,8 @@ extension FTPFileProvider {
}
return
}
completionHandler(nil)
}
}
}
+13 -6
View File
@@ -17,18 +17,25 @@ open class FileObject: Equatable {
self.allValues = allValues
}
internal init(url: URL, name: String, path: String) {
internal init(url: URL?, name: String, path: String) {
self.allValues = [URLResourceKey: Any]()
self.url = url
if let url = url {
self.url = url
}
self.name = name
self.path = path
}
/// URL to access the resource, can be a relative URL against base URL.
/// not supported by Dropbox provider.
open internal(set) var url: URL? {
open internal(set) var url: URL {
get {
return allValues[.fileURLKey] as? URL
if let url = allValues[.fileURLKey] as? URL {
return url
} else {
let path = self.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? self.path
return URL(string: path) ?? URL(string: "/")!
}
}
set {
allValues[.fileURLKey] = newValue
@@ -133,13 +140,13 @@ open class FileObject: Equatable {
/// Check `FileObject` equality
public static func ==(lhs: FileObject, rhs: FileObject) -> Bool {
if rhs === lhs {
if rhs === lhs {
return true
}
if type(of: lhs) != type(of: rhs) {
return false
}
if let rurl = rhs.url, let lurl = lhs.url {
if let rurl = rhs.allValues[.fileURLKey] as? URL, let lurl = lhs.allValues[.fileURLKey] as? URL {
return rurl == lurl
}
return rhs.path == lhs.path && rhs.size == lhs.size && rhs.modifiedDate == lhs.modifiedDate
+1 -1
View File
@@ -691,7 +691,7 @@ extension FileProviderBasic {
}
var i = number ?? 2
let similiar = contents.map {
$0.url?.lastPathComponent ?? $0.name
$0.url.lastPathComponent ?? $0.name
}.filter {
$0.hasPrefix(result)
}
+3 -2
View File
@@ -10,7 +10,7 @@ import Foundation
/// Containts path, url and attributes of a local file or resource.
public final class LocalFileObject: FileObject {
internal override init(url: URL, name: String, path: String) {
internal override init(url: URL?, name: String, path: String) {
super.init(url: url, name: name, path: path)
}
@@ -24,7 +24,8 @@ public final class LocalFileObject: FileObject {
if #available(iOS 9.0, macOS 10.11, tvOS 9.0, *) {
fileURL = URL(fileURLWithPath: rpath, relativeTo: relativeURL)
} else {
fileURL = URL(string: rpath.isEmpty ? "./" : rpath, relativeTo: relativeURL)
rpath = rpath.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? rpath
fileURL = URL(string: rpath, relativeTo: relativeURL) ?? relativeURL
}
if let fileURL = fileURL {
+3 -3
View File
@@ -18,11 +18,11 @@ public struct FileProviderOneDriveError: FileProviderHTTPError {
/// Containts path, url and attributes of a OneDrive file or resource.
public final class OneDriveFileObject: FileObject {
internal init(baseURL: URL?, name: String, path: String) {
var rpath = path
if path.hasPrefix("/") {
var rpath = path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? path
if rpath.hasPrefix("/") {
rpath.remove(at: rpath.startIndex)
}
let url = URL(string: rpath, relativeTo: baseURL) ?? URL(string: path)!
let url = URL(string: rpath, relativeTo: baseURL) ?? URL(string: rpath)!
super.init(url: url, name: name, path: path)
}
+6
View File
@@ -142,6 +142,12 @@ final public class SessionDelegate: NSObject, URLSessionDataDelegate, URLSession
if !(task is URLSessionDownloadTask), case FileOperationType.fetch = op {
return
}
if #available(iOSApplicationExtension 9.0, *) {
if task is URLSessionStreamTask {
return
}
}
DispatchQueue.main.async {
if error != nil {
fileProvider.delegate?.fileproviderFailed(fileProvider, operation: op)