I'm trying to upload a file as an attachment to my Frappe instance and running into a couple of problems. The first of which is related and a padding error is documented: in this question. I include it here just incase it is in some way part of the mistake i'm making.
My second problem, and the focus of this question is that when I upload my attachments they appear to be corrupt. A small xml file can be opened but is largely garbled. A larger PDF file will not even open. My assumption is i'm making some sort of beginner mistake with the base64 encoding.
Get data from local URL:
let settingsLocation = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("SettingsList.plist")
let fileData = try Data.init(contentsOf: settingsLocation)
This is then passed to the following function:
public func attachFileToCloudResource(resourceType: String, resourceName: String, attachment: Data) {
let fileAsString = (attachment.base64EncodedString().replacingOccurrences(of: "+", with: "%2B") + "==")
var request = URLRequest(url: URL(string: FRAPPE_INSTANCE + FRAPPE_METHODS + FRAPPE_UPLOAD_ATTACHMENT)!)
var components = URLComponents(url: request.url!, resolvingAgainstBaseURL: false)!
components.queryItems = [
URLQueryItem(name: FRAPPE_DOCTYPE, value: resourceType),
URLQueryItem(name: FRAPPE_DOCNAME, value: resourceName),
URLQueryItem(name: FRAPPE_FILENAME, value: "testFile.xml"),
URLQueryItem(name: FRAPPE_DATA, value: fileAsString),
URLQueryItem(name: FRAPPE_PRIVATE, value: "1"),
URLQueryItem(name: FRAPPE_DECODE_BASE64, value: "1")
]
let query = components.url!.query
request.httpMethod = "POST"
request.addValue("token \(API_KEY):\(API_SECRET)", forHTTPHeaderField: "Authorization")
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpBody = Data(query!.utf8)
// url session and dataTask send data to server below this point
}
This works correctly in that I get a positive response from the server and the file is attached to the specified record - but when the file is downloaded (via web browser) it seems to be corrupt regardless of what type of file I try.
Any help would be much appreciated.
I was able to find a solution that works for me.
First I used an online base64 encoder to encode a simple xml file. I then used the generated base64 string to re-create my POST using Postman (great tool, only just discovered it).
This worked and my file was attached to the specified record and not corrupted.
I then used the feature of Postman that generates code to see what it thought my base64String used for filedata should look like. I found that all the '+' characters were replaced with "%2B".
A bit of further investigation found that the query item in Swift does some of it's own percent encoding, however this answer gave an explanation why the '+' character is valid and not percent encoded by default.
The end result was that I removed my manual percent encoding from the original base64String:
let fileAsString = attachment.base64EncodedString() + "=="
And I added it when generating the httpBody from the query items:
request.httpBody = Data(query!.replacingOccurrences(of: "+", with: "%2B").utf8)