Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Upload a file via FTP on iOS or macOS
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)
}
}
@Patrickyp

This comment has been minimized.

Copy link

Patrickyp commented Sep 6, 2017

Works perfectly thank you sir.

@Saleh-At

This comment has been minimized.

Copy link

Saleh-At commented Nov 9, 2017

Hello there
Can you guide me to download files (also directory) from ftp?
thx

@rajan2211

This comment has been minimized.

Copy link

rajan2211 commented Nov 15, 2017

I am able to upload small images, large images are not getting uploaded correctly. Only upper part is visible and the lower part is getting trimmed. Any help?

@reenaphilip

This comment has been minimized.

Copy link

reenaphilip commented Dec 7, 2017

'kCFStreamPropertyFTPUserName' was deprecated in iOS 9.0: Use NSURLSessionAPI for ftp requests
I am getting this warning. :(

@Gondomir

This comment has been minimized.

Copy link

Gondomir commented Mar 14, 2018

Could you please rewrite this to NSURLSessionAPI, because I get also the warnings. Thanks!

@Gondomir

This comment has been minimized.

Copy link

Gondomir commented Mar 16, 2018

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)
  

@benezor

This comment has been minimized.

Copy link

benezor commented Apr 13, 2018

Hello,

Can you post an example of how you call that method?

thanks

@tsc9

This comment has been minimized.

Copy link

tsc9 commented Jun 1, 2018

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?

@renatoucha

This comment has been minimized.

Copy link

renatoucha commented Jun 9, 2018

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?

@tsc9

This comment has been minimized.

Copy link

tsc9 commented Jun 13, 2018

test12345

<script>alert(‘test’)</script>

test56789

@robin850

This comment has been minimized.

Copy link

robin850 commented Jul 27, 2018

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.

@Mamathapardhasaradhi

This comment has been minimized.

Copy link

Mamathapardhasaradhi commented May 22, 2019

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

@laputa0122

This comment has been minimized.

Copy link

laputa0122 commented Oct 17, 2019

Great example and modification @Gondomir, still work in 2019 Oct.

@maitripatel347

This comment has been minimized.

Copy link

maitripatel347 commented Dec 3, 2019

Hello,
Can you please provide me code suggestion to download files (also directory) from ftp?
thank you

@sk-chanch

This comment has been minimized.

Copy link

sk-chanch commented Apr 9, 2020

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)
                
            }
            
           
        }
    }`
@loldi

This comment has been minimized.

Copy link

loldi commented May 11, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.