-
-
Save Nirma/fb9991be776107d17fdcd6ed2aa02876 to your computer and use it in GitHub Desktop.
import Foundation | |
import CFNetwork | |
public class FTPUpload { | |
fileprivate let ftpBaseUrl: String | |
fileprivate let directoryPath: String | |
fileprivate let username: String | |
fileprivate let password: String | |
public init(baseUrl: String, userName: String, password: String, directoryPath: String) { | |
self.ftpBaseUrl = baseUrl | |
self.username = userName | |
self.password = password | |
self.directoryPath = directoryPath | |
} | |
} | |
// MARK: - Steam Setup | |
extension FTPUpload { | |
private func setFtpUserName(for ftpWriteStream: CFWriteStream, userName: CFString) { | |
let propertyKey = CFStreamPropertyKey(rawValue: kCFStreamPropertyFTPUserName) | |
CFWriteStreamSetProperty(ftpWriteStream, propertyKey, userName) | |
} | |
private func setFtpPassword(for ftpWriteStream: CFWriteStream, password: CFString) { | |
let propertyKey = CFStreamPropertyKey(rawValue: kCFStreamPropertyFTPPassword) | |
CFWriteStreamSetProperty(ftpWriteStream, propertyKey, password) | |
} | |
fileprivate func ftpWriteStream(forFileName fileName: String) -> CFWriteStream? { | |
let fullyQualifiedPath = "ftp://\(ftpBaseUrl)/\(directoryPath)/\(fileName)" | |
guard let ftpUrl = CFURLCreateWithString(kCFAllocatorDefault, fullyQualifiedPath as CFString, nil) else { return nil } | |
let ftpStream = CFWriteStreamCreateWithFTPURL(kCFAllocatorDefault, ftpUrl) | |
let ftpWriteStream = ftpStream.takeRetainedValue() | |
setFtpUserName(for: ftpWriteStream, userName: username as CFString) | |
setFtpPassword(for: ftpWriteStream, password: password as CFString) | |
return ftpWriteStream | |
} | |
} | |
// MARK: - FTP Write | |
extension FTPUpload { | |
public func send(data: Data, with fileName: String, success: @escaping ((Bool)->Void)) { | |
guard let ftpWriteStream = ftpWriteStream(forFileName: fileName) else { | |
success(false) | |
return | |
} | |
if CFWriteStreamOpen(ftpWriteStream) == false { | |
print("Could not open stream") | |
success(false) | |
return | |
} | |
let fileSize = data.count | |
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: fileSize) | |
data.copyBytes(to: buffer, count: fileSize) | |
defer { | |
CFWriteStreamClose(ftpWriteStream) | |
buffer.deallocate(capacity: fileSize) | |
} | |
var offset: Int = 0 | |
var dataToSendSize: Int = fileSize | |
repeat { | |
let bytesWritten = CFWriteStreamWrite(ftpWriteStream, &buffer[offset], dataToSendSize) | |
if bytesWritten > 0 { | |
offset += bytesWritten.littleEndian | |
dataToSendSize -= bytesWritten | |
continue | |
} else if bytesWritten < 0 { | |
// ERROR | |
print("FTPUpload - ERROR") | |
break | |
} else if bytesWritten == 0 { | |
// SUCCESS | |
print("FTPUpload - Completed!!") | |
break | |
} | |
} while CFWriteStreamCanAcceptBytes(ftpWriteStream) | |
success(true) | |
} | |
} |
For me, it also didn't work for larger files, here is a fix (change this in extension FTPUpload):
var offset: Int = 0
var dataToSendSize: Int = fileSize
var shouldContinue = true
repeat {
if (CFWriteStreamCanAcceptBytes(ftpWriteStream)) {
let bytesWritten = CFWriteStreamWrite(ftpWriteStream, &buffer[offset], dataToSendSize)
print("ftp bytes written: \(bytesWritten)")
if bytesWritten > 0 {
offset += bytesWritten.littleEndian
dataToSendSize -= bytesWritten
continue
} else if bytesWritten < 0 {
// ERROR
print("FTPUpload - ERROR")
shouldContinue = false
break
} else if bytesWritten == 0 {
// SUCCESS
print("FTPUpload - Completed!!")
shouldContinue = false
break
}
} else {
usleep(200000)
print(".", separator: "", terminator: "")
}
} while shouldContinue
success(true)
Hello,
Can you post an example of how you call that method?
thanks
I want to upload an audio file to server 120.1xx.1xx.1xx , username = "abcd" , password = "wxyz",
audio file: tmpURL = URL(fileURLWithPath: NSTemporaryDirectory() + "audio.wav")
someone please tell me how to use this code?
Same issue as rajan2211. (I am able to upload small m4a files, large files are not getting uploaded correctly.)
Tried Gondomir solution but it didn't work.
Any suggestion?
test12345
<script>alert(‘test’)</script>test56789
Hi there,
Thank you very much for sharing your code @Nirma !
Actually the file wasn't send properly in my case as well but I found this StackOverflow answer useful ; the while
should be changed to while true
and the break
will properly stop the loop once there is no bytes to send anymore.
Hello Nirma,
Thank you for sharing this code that too in swift. This saves me a day.
I want to implement pause, resume and cancel for the above upload functionality. Could you please help me out to implement pause, resume and cancel.
Thanks in advance,
Mamatha
Great example and modification @Gondomir, still work in 2019 Oct.
Hello,
Can you please provide me code suggestion to download files (also directory) from ftp?
thank you
Fix for freeze when upload.
`
DispatchQueue.global(qos: .background).async {[weak self] in
guard let _self = self else{return}
let fileSize = data.count
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: fileSize)
data.copyBytes(to: buffer, count: fileSize)
defer {
CFWriteStreamClose(ftpWriteStream)
buffer.deallocate()
}
var offset: Int = 0
var dataToSendSize: Int = fileSize
var shouldContinue = true
while shouldContinue {
let bytesWritten = CFWriteStreamWrite(ftpWriteStream, &buffer[offset], dataToSendSize)
print("ftp bytes written: \(bytesWritten)")
if bytesWritten > 0 {
offset += bytesWritten.littleEndian
dataToSendSize -= bytesWritten
} else if bytesWritten < 0 {
// ERROR
print("FTPUpload - ERROR with \(CFWriteStreamGetError(ftpWriteStream))")
shouldContinue = false
success(false)
} else if bytesWritten == 0 {
// SUCCESS
print( "FTPUpload - Completed!!")
shouldContinue = false
success(true)
}
}
}`
Can anyone assist with using this to upload a local video file on the device to FTP?
I've got it working with an image, just curious how I can arrange it to upload a .mp4/.mov file.
Can anyone assist with using this to upload a local video file on the device to FTP?
I've got it working with an image, just curious how I can arrange it to upload a .mp4/.mov file.
Can you tell me how it works with image or txt file?
I cannot find the right data type for the send function.
Thanks!!
Can anyone assist with using this to upload a local video file on the device to FTP?
I've got it working with an image, just curious how I can arrange it to upload a .mp4/.mov file.Can you tell me how it works with image or txt file?
I cannot find the right data type for the send function.
Thanks!!
Sure, for a .txt file, make sure the file is added to your project. To upload the file you would use:
let ftp = FTPUpload(baseUrl: String, userName: String, password: String, directoryPath: String)
do {
let data = try Data(contentsOf: url (this is your .txt file location as a URL) as URL, options: [ .alwaysMapped, .uncached])
ftp.send(data: data , with: "name.txt") { (success) in
print("success")
}
print(data)
}
catch {
print(error)
}
}
Hope this helps!
Can anyone assist with using this to upload a local video file on the device to FTP?
I've got it working with an image, just curious how I can arrange it to upload a .mp4/.mov file.Can you tell me how it works with image or txt file?
I cannot find the right data type for the send function.
Thanks!!Sure, for a .txt file, make sure the file is added to your project. To upload the file you would use:
let ftp = FTPUpload(baseUrl: String, userName: String, password: String, directoryPath: String) do { let data = try Data(contentsOf: url (this is your .txt file location as a URL) as URL, options: [ .alwaysMapped, .uncached]) ftp.send(data: data , with: "name.txt") { (success) in print("success") } print(data) } catch { print(error) } }
Hope this helps!
I think I got it. But I have a problem with file access.
I got error message, although, I have switched off the AppSandbox already.
Error Domain=NSCocoaErrorDomain Code=257 "The file “XXX” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/Users/XXX, NSUnderlyingError=0x600000c8d4d0 {Error Domain=NSPOSIXErrorDomain Code=13 "Permission denied"}}
It seems I don't have access to that file?
repeat {
let bytesWritten = CFWriteStreamWrite(ftpWriteStream, &buffer[offset], dataToSendSize)
if bytesWritten > 0 {
offset += bytesWritten.littleEndian
dataToSendSize -= bytesWritten
continue
} else if bytesWritten < 0 {
// ERROR
print("FTPUpload - ERROR")
break
} else if bytesWritten == 0 {
// SUCCESS
print("FTPUpload - Completed!!")
break
}
} while CFWriteStreamCanAcceptBytes(ftpWriteStream)
success(true)
}
I am getting -1 in bytesWritten that why file is written and I have check dataToSendSize is 8000 in size but bytesWritten getting -1 in first time that why file in writing.
Please help me.
Thanks
Works perfectly thank you sir.
not working for me bytesWritten return -1 at first time
Can anyone assist with using this to upload a local video file on the device to FTP?
I've got it working with an image, just curious how I can arrange it to upload a .mp4/.mov file.Can you tell me how it works with image or txt file?
I cannot find the right data type for the send function.
Thanks!!Sure, for a .txt file, make sure the file is added to your project. To upload the file you would use:
let ftp = FTPUpload(baseUrl: String, userName: String, password: String, directoryPath: String) do { let data = try Data(contentsOf: url (this is your .txt file location as a URL) as URL, options: [ .alwaysMapped, .uncached]) ftp.send(data: data , with: "name.txt") { (success) in print("success") } print(data) } catch { print(error) } }
Hope this helps!
I think I got it. But I have a problem with file access.
I got error message, although, I have switched off the AppSandbox already.Error Domain=NSCocoaErrorDomain Code=257 "The file “XXX” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/Users/XXX, NSUnderlyingError=0x600000c8d4d0 {Error Domain=NSPOSIXErrorDomain Code=13 "Permission denied"}}
It seems I don't have access to that file?
Are you trying to access the file on your development machine? Make sure you've added the file you are trying to access to your xcode project file. That might be the cause.
Can anyone assist with using this to upload a local video file on the device to FTP?
I've got it working with an image, just curious how I can arrange it to upload a .mp4/.mov file.Can you tell me how it works with image or txt file?
I cannot find the right data type for the send function.
Thanks!!Sure, for a .txt file, make sure the file is added to your project. To upload the file you would use:
let ftp = FTPUpload(baseUrl: String, userName: String, password: String, directoryPath: String) do { let data = try Data(contentsOf: url (this is your .txt file location as a URL) as URL, options: [ .alwaysMapped, .uncached]) ftp.send(data: data , with: "name.txt") { (success) in print("success") } print(data) } catch { print(error) } }
Hope this helps!
I think I got it. But I have a problem with file access.
I got error message, although, I have switched off the AppSandbox already.
Error Domain=NSCocoaErrorDomain Code=257 "The file “XXX” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/Users/XXX, NSUnderlyingError=0x600000c8d4d0 {Error Domain=NSPOSIXErrorDomain Code=13 "Permission denied"}}
It seems I don't have access to that file?Are you trying to access the file on your development machine? Make sure you've added the file you are trying to access to your xcode project file. That might be the cause.
But I would like to upload a file each time when I create by my app. So it is no possible for me to add those files into the project file, right? Is there another way? Is there another method to access files?
### Now working for me.
How create folder in server?
How to download/view file from FTP Server.
How create folder in server?
Manually
Now working for me.
I have solved my problem but facing the same problem as you.
The bytesWritten equals to -1 and it is not going to upload anything...
Did you solve your problem?
Works perfectly thank you sir.
not working for me bytesWritten return -1 at first time
I got it. I have looked through the meaning of bytesWritten equals to -1 and it turns out there is error by connection.
By my side it is maybe because of too many connections already existed so I restart my device and it worked then.
anyway, the bytesWritten equals to -1 doesn't mean you have a problem with the file, but it is mostly because of the connection.
Works perfectly thank you sir.
not working for me bytesWritten return -1 at first time
I got it. I have looked through the meaning of bytesWritten equals to -1 and it turns out there is error by connection.
By my side it is maybe because of too many connections already existed so I restart my device and it worked then.anyway, the bytesWritten equals to -1 doesn't mean you have a problem with the file, but it is mostly because of the connection.
It can be because of problem in credentials.
Is there any Lib or same as above class that we can get file URL so that we can load the url or download that attachment?
Could you please rewrite this to NSURLSessionAPI, because I get also the warnings. Thanks!