在 Swift 中实施 FTP 文件传输 (STOR)?

Implementing FTP file transfer (STOR) in Swift?

我正在开发用 Swift 编写的客户端 iOS 应用程序,它允许用户在 FTP 上传输文件服务器。我知道 Apple 提供 API 来执行此操作,但自 2016 年以来它们已被弃用,我需要一个替代方案。我也知道 FTP 不安全,但我必须这样做。我的目标是通过服务器打开一个套接字并向它发送命令以传输文件。我正在使用 BlueSocket 但我无法获取它。这是我的代码:

do {
    let socket = try Socket.create()
                    
    try socket.connect(to: "127.0.0.1", port: 21)
    let answer = try socket.readString()
    print(answer!)
                    
    try socket.write(from: "USER test\r\n")
    let userAnswer = try socket.readString()
    print(userAnswer!)
                    
    try socket.write(from: "PASS 0000\r\n")
    let passwordAnswer = try socket.readString()
    print(passwordAnswer!)
                    
    try socket.write(from: "PASV\r\n")
    let passiveModeAnswer = try socket.readString()
    print(passiveModeAnswer!)
                    
    try socket.write(from: "STOR myFile.txt\r\n")
    let storAnswer = try socket.readString()
    print(storAnswer!)

    /* CODE FOR SENDING FILE BELOW */

} catch let error as NSError {
    print(error.debugDescription)
}

我目前正在我的本地服务器上工作,我成功登录了。服务器已准备好接受数据。我在控制台中看到了这个:

150 Ok to receive data.

这是我用来发送文件的代码:

guard let path = Bundle.main.path(forResource: "myFile", ofType: "txt"),
      let fileInputStream = InputStream(fileAtPath: path) 
else {
    return
}
            
fileInputStream.open()

var buffer = [UInt8](repeating: 0, count: 1024)
var bytesRead = 0
var fileData = Data()

while fileInputStream.hasBytesAvailable {
    bytesRead = fileInputStream.read(&buffer, maxLength: buffer.count)
    fileData.append(buffer, count: bytesRead)
}

try socket.write(from: fileData)
let fileAnswer = try socket.readString()
print(fileAnswer!)

fileInputStream.close()
socket.close()

执行后,我在本地服务器中看到“myFile.txt”,但它是空的。原始文件存储在 Xcode 中:它是一个 13 字节的文件(一个简单的文本文件)。我看到断点 bytesRead 更改为 13 并且 fileData 被赋值。在 try socket.write(from: fileData) 指令之后,我没有收到来自套接字的任何响应,但只有这条错误消息:Error code: -9982(0x-26FE), Interrupted system call。有什么遗漏或错误吗?我在实现目标方面做得很好吗?


正如 Martin 正确所说,我已经解析了 PASV 答案的结果,这是代码:

guard let path = Bundle.main.path(forResource: "myFile", ofType: "txt"),
      let fileInputStream = InputStream(fileAtPath: path) 
else {
    return
}
            
fileInputStream.open()
            
guard let pasvAnswer = passiveModeAnswer else {
    return
}
            
var editedAnswer = String(pasvAnswer.dropFirst(26))
editedAnswer = editedAnswer.replacingOccurrences(of: ")", with: "")
editedAnswer = editedAnswer.replacingOccurrences(of: "(", with: "")
editedAnswer = editedAnswer.replacingOccurrences(of: ",", with: ".")
editedAnswer = String(editedAnswer.dropLast())
let cmps = editedAnswer.components(separatedBy: ".")
            
let dataHost = cmps[0] + "." + cmps[1] + "." + cmps[2] + "." + cmps[3]
let dataPort = ((Int(cmps[4]))!<<8) + Int(cmps[5])!
            
print("dataHost: \(dataHost), dataPort: \(dataPort)")
            
try socket.write(from: "STOR myFile.txt\r\n")
let storAnswer = try socket.readString()
print(storAnswer!)
            
let dataSocket = try Socket.create()
try dataSocket.connect(to: dataHost, port: Int32(dataPort))
            
var buffer = [UInt8](repeating: 0, count: 1024)
var bytesRead = 0
var fileData = Data()

while fileInputStream.hasBytesAvailable {
    bytesRead = fileInputStream.read(&buffer, maxLength: buffer.count)
    fileData.append(buffer, count: bytesRead)
}
            
try dataSocket.write(from: fileData)
let fileAnswer = try dataSocket.readString()
print(fileAnswer!)
            
fileInputStream.close()
dataSocket.close()
socket.close()

我观察到只有当我终止应用程序时文件才会完全传输。有什么想法吗?

您必须解析 PASV 响应 (passiveModeAnswer)。并连接到提供的 IP 地址和端口并将文件内容发送到新连接。