swift/Sources/TeaFileForm/Models.swift (146 lines of code) (raw):

import Foundation import Tea public class FileField : Tea.TeaModel { public var filename: String? public var contentType: String? public var content: InputStream? public override init() { super.init() } public init(_ dict: [String: Any]) { super.init() self.fromMap(dict) } public override func validate() throws -> Void { try self.validateRequired(self.filename, "filename") try self.validateRequired(self.contentType, "contentType") try self.validateRequired(self.content, "content") } public override func toMap() -> [String : Any] { var map = super.toMap() if self.filename != nil { map["filename"] = self.filename! } if self.contentType != nil { map["contentType"] = self.contentType! } if self.content != nil { map["content"] = self.content! } return map } public override func fromMap(_ dict: [String: Any]) -> Void { if dict.keys.contains("filename") && dict["filename"] != nil { self.filename = dict["filename"] as! String } if dict.keys.contains("contentType") && dict["contentType"] != nil { self.contentType = dict["contentType"] as! String } if dict.keys.contains("content") && dict["content"] != nil { self.content = dict["content"] as! InputStream } } } class FileForm: InputStream { private var form: [String: Any] private var boundary: String private var keys: [String] private var keyIndex = 0 private var fileNumber = 0 private var files: [FileField] = [] private var fileBodyStream: InputStream? private var temporaryStream: InputStream? private var temporaryEndStream: InputStream? private var endInputStream: InputStream? public init(_ form: [String: Any], _ boundary: String) { self.form = form self.boundary = boundary self.keys = Array(form.keys) self.keys = self.keys.sorted() super.init(data: Data()) self.endInputStream = InputStream(data: "--\(boundary)--\r\n".data(using: .utf8)!) endInputStream?.open() prepareNextPart() } override func read(_ buffer: UnsafeMutablePointer<UInt8>, maxLength len: Int) -> Int { fileBodyStream?.open() defer { fileBodyStream?.close() } var bytesRead = fileBodyStream?.read(buffer, maxLength: len) ?? 0 if bytesRead > 0 { return bytesRead } temporaryStream?.open() defer { temporaryStream?.close() } bytesRead = temporaryStream?.read(buffer, maxLength: len) ?? 0 if bytesRead > 0 { return bytesRead } temporaryEndStream?.open() defer { temporaryEndStream?.close() } bytesRead = temporaryEndStream?.read(buffer, maxLength: len) ?? 0 if bytesRead > 0 { return bytesRead } if fileNumber > 0 || keyIndex < keys.count { prepareNextPart() return read(buffer, maxLength: len) } if endInputStream?.hasBytesAvailable == true { return endInputStream!.read(buffer, maxLength: len) } return -1 } override var hasBytesAvailable: Bool { if keys.count <= 0 { return false } return fileBodyStream?.hasBytesAvailable ?? false || temporaryStream?.hasBytesAvailable ?? false || temporaryEndStream?.hasBytesAvailable ?? false || endInputStream?.hasBytesAvailable ?? false || keyIndex < keys.count || fileNumber > 0 } override open func close() { fileBodyStream?.close() temporaryStream?.close() temporaryEndStream?.close() endInputStream?.close() } private func prepareNextPart() { guard keyIndex < keys.count else { if fileNumber > 0 { prepareFilePart() } return } let key = keys[keyIndex] let value = form[key]! if let fileField = value as? FileField { files.append(fileField) fileNumber += 1 keyIndex += 1 prepareFilePart() } else { let contentString = "--\(boundary)\r\nContent-Disposition: form-data; name=\"\(key)\"\r\n\r\n\(value)\r\n" fileBodyStream?.close() fileBodyStream = InputStream(data: contentString.data(using: .utf8)!) keyIndex += 1 } } private func prepareFilePart() { guard fileNumber > 0 else { return } let fileField = files.removeLast() fileNumber -= 1 if fileField.content != nil { let fileHeaderString = "--\(boundary)\r\nContent-Disposition: form-data; name=\"file\"; filename=\"\(fileField.filename!)\"\r\nContent-Type: \(fileField.contentType!)\r\n\r\n" fileBodyStream?.close() fileBodyStream = InputStream(data: fileHeaderString.data(using: .utf8)!) temporaryStream?.close() temporaryStream = fileField.content temporaryEndStream?.close() temporaryEndStream = InputStream(data: "\r\n".data(using: .utf8)!) } } }