Swift 正在解压缩 zip 文件并从 Gmail API 的 base64 数据中找到 xml 文件
Swift Unzipping zip file and find xml file from base64 data from Gmail API
这个问题是关于 iOS 13 中使用 SwiftUI 和 Gmail API 的 DMARC 报告查看器应用程序。报告通过 google 以 xml 格式邮寄到我们的管理员电子邮件 ID,将被压缩。所以基本上它是一个 zip 附件。所以在这里,GMail API 用于使用过滤器访问那些特定的邮件,并从 API 获取所有 base64 编码的数据。还将其解码为数据类型数据。那远没问题。下一部分是解压缩字节格式的 zip 文件的数据,并以 String 类型提取其中的 xml 文件。然后我需要解析XML。我想我可以用 XMLParser.
来解析
问题:如何解压 Data
类型的 zip 文件并从中获取 String
类型的 xml 文件?
INPUT: String in Base64 format from GMail API fetch (A zip file attachment with only 1 xml file inside)
OUTPUT: String in XML format
PLATFORM: iOS 13/Swift 5.2/SwiftUI/Xcode 11.4
ACTION:
(INPUT)
base64: String | Decode -> Data
attachment.zip: Data | Decompress -> [Data]
ListOfFiles: [Data] | FirstIndex -> Data
dmarc.xml: Data | ContentOfXML -> String
(OUTPUT)
更新: 我尝试了一个名为 Zip 的外部包,但它也失败了。
let path = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let url = path.appendingPathComponent(messageId+".zip")
do {
try data.write(to: url)
} catch {
print("Error while writing: "+error.localizedDescription)
}
do {
let unzipDirectory = try Zip.quickUnzipFile(url)
print(unzipDirectory)
} catch let error as NSError {
print("Error while unzipping: "+error.localizedDescription)
}
此代码导致以下错误
Error while unzipping: The operation couldn’t be completed. (Zip.ZipError error 1.)
终于找到了。正如 中提到的,电子邮件正文以 7 位 US-ASCII 数据编码。所以这就是base64解码不起作用的原因。
定义在rfc1341:
An encoding type of 7BIT requires that the body is already in a
seven-bit mail- ready representation. This is the default value --
that is, "Content-Transfer-Encoding: 7BIT" is assumed if the
Content-Transfer-Encoding header field is not present.
添加以下内容后整个代码有效。
let edata: String = result.data.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/")
正如 Ref 2 中提到的,它只需要在从 gmail api 接收到的 base64 数据中将 '-' 字符替换为 '+' 并将 '_' 字符替换为 '/'。 =18=]
func getAttachedData(messageId: String, attachmentId: String) {
decode(self.urlBase+messageId+"/attachments/"+attachmentId+"?"+self.urlKey) { (result: Attachment) in
let edata: String = result.data.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/")
if let data = Data(base64Encoded: edata, options: .ignoreUnknownCharacters) {
let filemanager = FileManager.default
let path = try! filemanager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let url = path.appendingPathComponent(messageId+".zip")
do {
try data.write(to: url)
} catch {
print("Error while writing: "+error.localizedDescription)
}
do {
let unzipDirectory = try Zip.quickUnzipFile(url)
print("Unzipped")
do {
let filelist = try filemanager.contentsOfDirectory(at: unzipDirectory, includingPropertiesForKeys: [], options: [])
for filename in filelist {
print(filename.lastPathComponent)
print(filename.relativeString)
do {
let text = try String(contentsOf: filename, encoding: .utf8)
print(text)
DispatchQueue.main.async {
self.attachments.append(text)
}
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
}
}
} catch let error {
print("Error: \(error.localizedDescription)")
}
} catch let error as NSError {
print("Error while unzipping: "+error.localizedDescription)
}
}
}
}
参考 1:
参考 2:
这个问题是关于 iOS 13 中使用 SwiftUI 和 Gmail API 的 DMARC 报告查看器应用程序。报告通过 google 以 xml 格式邮寄到我们的管理员电子邮件 ID,将被压缩。所以基本上它是一个 zip 附件。所以在这里,GMail API 用于使用过滤器访问那些特定的邮件,并从 API 获取所有 base64 编码的数据。还将其解码为数据类型数据。那远没问题。下一部分是解压缩字节格式的 zip 文件的数据,并以 String 类型提取其中的 xml 文件。然后我需要解析XML。我想我可以用 XMLParser.
来解析问题:如何解压 Data
类型的 zip 文件并从中获取 String
类型的 xml 文件?
INPUT: String in Base64 format from GMail API fetch (A zip file attachment with only 1 xml file inside)
OUTPUT: String in XML format
PLATFORM: iOS 13/Swift 5.2/SwiftUI/Xcode 11.4
ACTION:
(INPUT)
base64: String | Decode -> Data
attachment.zip: Data | Decompress -> [Data]
ListOfFiles: [Data] | FirstIndex -> Data
dmarc.xml: Data | ContentOfXML -> String
(OUTPUT)
更新: 我尝试了一个名为 Zip 的外部包,但它也失败了。
let path = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let url = path.appendingPathComponent(messageId+".zip")
do {
try data.write(to: url)
} catch {
print("Error while writing: "+error.localizedDescription)
}
do {
let unzipDirectory = try Zip.quickUnzipFile(url)
print(unzipDirectory)
} catch let error as NSError {
print("Error while unzipping: "+error.localizedDescription)
}
此代码导致以下错误
Error while unzipping: The operation couldn’t be completed. (Zip.ZipError error 1.)
终于找到了。正如
定义在rfc1341:
An encoding type of 7BIT requires that the body is already in a seven-bit mail- ready representation. This is the default value -- that is, "Content-Transfer-Encoding: 7BIT" is assumed if the Content-Transfer-Encoding header field is not present.
添加以下内容后整个代码有效。
let edata: String = result.data.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/")
正如 Ref 2 中提到的,它只需要在从 gmail api 接收到的 base64 数据中将 '-' 字符替换为 '+' 并将 '_' 字符替换为 '/'。 =18=]
func getAttachedData(messageId: String, attachmentId: String) {
decode(self.urlBase+messageId+"/attachments/"+attachmentId+"?"+self.urlKey) { (result: Attachment) in
let edata: String = result.data.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/")
if let data = Data(base64Encoded: edata, options: .ignoreUnknownCharacters) {
let filemanager = FileManager.default
let path = try! filemanager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let url = path.appendingPathComponent(messageId+".zip")
do {
try data.write(to: url)
} catch {
print("Error while writing: "+error.localizedDescription)
}
do {
let unzipDirectory = try Zip.quickUnzipFile(url)
print("Unzipped")
do {
let filelist = try filemanager.contentsOfDirectory(at: unzipDirectory, includingPropertiesForKeys: [], options: [])
for filename in filelist {
print(filename.lastPathComponent)
print(filename.relativeString)
do {
let text = try String(contentsOf: filename, encoding: .utf8)
print(text)
DispatchQueue.main.async {
self.attachments.append(text)
}
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
}
}
} catch let error {
print("Error: \(error.localizedDescription)")
}
} catch let error as NSError {
print("Error while unzipping: "+error.localizedDescription)
}
}
}
}
参考 1:
参考 2: