在 Swift 中解析多部分 SOAP 响应
Parse a multipart SOAP response in Swift
我正在尝试从 soap 服务下载文件,并且在成功请求后得到以下信息
响应头
multipart/related; type="application/xop+xml"; boundary="uuid:917b60a9-3089-43ad-a8c2-b4a3c62db98c"; start="<root.message@cxf.apache.org>"; start-info="text/xml"
响应正文
--uuid:0a679f64-0753-44fe-b627-2267b5b72b1d
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"
Content-Transfer-Encoding: binary
Content-ID: <root.message@cxf.apache.org>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:leseDokumentResponse xmlns:ns2="http://webservice/"><return><status>OK</status><dokument><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:c58d3315-9cb6-413a-98a1-5a29671cfdb6-41@cxf.apache.org"/></dokument></return></ns2:leseDokumentResponse></soap:Body></soap:Envelope>
--uuid:0a679f64-0753-44fe-b627-2267b5b72b1d
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <c58d3315-9cb6-413a-98a1-5a29671cfdb6-41@cxf.apache.org>
... binary data ...
--uuid:0a679f64-0753-44fe-b627-2267b5b72b1d--
如何解析文件二进制数据?
我尝试了这些问题,但没有任何效果 Parse multipart response for image download in ios
使用 MultipartKit
可以轻松解析多部分 SOAP 响应
我在 Alamofire
的 DataResponse
上创建了一个扩展来处理响应
import Alamofire
import MultipartKit
import Swime
extension DataResponse where Success == Data {
var boundary: String? {
let header = response?.allHeaderFields.first(where: {
[=10=].key as? String == "Content-Type"
})
let scanner = Scanner(string: header?.value as? String ?? "")
_ = scanner.scanUpToString("boundary=\"")
_ = scanner.scanString("boundary=\"")
let boundary = scanner.scanUpToString("\";")
return boundary
}
func multipartParts() -> [MultipartPart] {
guard let boundary = boundary else { return [] }
guard let data = try? result.get() else { return [] }
let parser = MultipartParser(boundary: boundary)
var parts: [MultipartPart] = []
var headers: HTTPHeaders = [:]
var body: Data = Data()
parser.onHeader = { (field, value) in
headers.replaceOrAdd(name: field, value: value)
}
parser.onBody = { new in
body.append(contentsOf: new.readableBytesView)
}
parser.onPartComplete = {
let part = MultipartPart(headers: headers, body: body)
headers = [:]
body = Data()
parts.append(part)
}
do {
try parser.execute(data)
} catch {
print(error.localizedDescription)
}
return parts
}
func parseMultipart() -> (message: String, files: [Data])? {
let parts = multipartParts()
let message = parts.first(where: { [=10=].id == "<root.message@cxf.apache.org>" })?.body.string ?? ""
let dataPart = parts.filter { [=10=].id != "<root.message@cxf.apache.org>" }
let files = dataPart.compactMap { Data(multipart: [=10=]) }
return (message, files)
}
}
extension ByteBuffer {
var string: String {
return String(decoding: self.readableBytesView, as: UTF8.self)
}
}
extension MultipartPart {
/// Gets or sets the `name` attribute from the part's `"Content-ID"` header.
public var id: String? {
get { self.headers.first(name: "Content-ID") }
}
}
然后可以像这样用于数据响应:
let request = SessionManager.request(urlRequest).responseData(completionHandler: { response in
switch response.result {
case .failure(let error):
self.alert(error)
case .success:
guard let parts = response.parseMultipart() else { return }
DispatchQueue.main.async {
self.loadResponse(message: parts.message, files: parts.files)
}
}
})
消息 ID 可能不同,所以我可能建议也检查类型
希望这对某人有所帮助。
我正在尝试从 soap 服务下载文件,并且在成功请求后得到以下信息
响应头
multipart/related; type="application/xop+xml"; boundary="uuid:917b60a9-3089-43ad-a8c2-b4a3c62db98c"; start="<root.message@cxf.apache.org>"; start-info="text/xml"
响应正文
--uuid:0a679f64-0753-44fe-b627-2267b5b72b1d
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"
Content-Transfer-Encoding: binary
Content-ID: <root.message@cxf.apache.org>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:leseDokumentResponse xmlns:ns2="http://webservice/"><return><status>OK</status><dokument><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:c58d3315-9cb6-413a-98a1-5a29671cfdb6-41@cxf.apache.org"/></dokument></return></ns2:leseDokumentResponse></soap:Body></soap:Envelope>
--uuid:0a679f64-0753-44fe-b627-2267b5b72b1d
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <c58d3315-9cb6-413a-98a1-5a29671cfdb6-41@cxf.apache.org>
... binary data ...
--uuid:0a679f64-0753-44fe-b627-2267b5b72b1d--
如何解析文件二进制数据?
我尝试了这些问题,但没有任何效果 Parse multipart response for image download in ios
使用 MultipartKit
可以轻松解析多部分 SOAP 响应我在 Alamofire
的 DataResponse
上创建了一个扩展来处理响应
import Alamofire
import MultipartKit
import Swime
extension DataResponse where Success == Data {
var boundary: String? {
let header = response?.allHeaderFields.first(where: {
[=10=].key as? String == "Content-Type"
})
let scanner = Scanner(string: header?.value as? String ?? "")
_ = scanner.scanUpToString("boundary=\"")
_ = scanner.scanString("boundary=\"")
let boundary = scanner.scanUpToString("\";")
return boundary
}
func multipartParts() -> [MultipartPart] {
guard let boundary = boundary else { return [] }
guard let data = try? result.get() else { return [] }
let parser = MultipartParser(boundary: boundary)
var parts: [MultipartPart] = []
var headers: HTTPHeaders = [:]
var body: Data = Data()
parser.onHeader = { (field, value) in
headers.replaceOrAdd(name: field, value: value)
}
parser.onBody = { new in
body.append(contentsOf: new.readableBytesView)
}
parser.onPartComplete = {
let part = MultipartPart(headers: headers, body: body)
headers = [:]
body = Data()
parts.append(part)
}
do {
try parser.execute(data)
} catch {
print(error.localizedDescription)
}
return parts
}
func parseMultipart() -> (message: String, files: [Data])? {
let parts = multipartParts()
let message = parts.first(where: { [=10=].id == "<root.message@cxf.apache.org>" })?.body.string ?? ""
let dataPart = parts.filter { [=10=].id != "<root.message@cxf.apache.org>" }
let files = dataPart.compactMap { Data(multipart: [=10=]) }
return (message, files)
}
}
extension ByteBuffer {
var string: String {
return String(decoding: self.readableBytesView, as: UTF8.self)
}
}
extension MultipartPart {
/// Gets or sets the `name` attribute from the part's `"Content-ID"` header.
public var id: String? {
get { self.headers.first(name: "Content-ID") }
}
}
然后可以像这样用于数据响应:
let request = SessionManager.request(urlRequest).responseData(completionHandler: { response in
switch response.result {
case .failure(let error):
self.alert(error)
case .success:
guard let parts = response.parseMultipart() else { return }
DispatchQueue.main.async {
self.loadResponse(message: parts.message, files: parts.files)
}
}
})
消息 ID 可能不同,所以我可能建议也检查类型
希望这对某人有所帮助。