Search code examples
iosswiftnsdatansmutabledata

Swift - Sending binary file to a server (convert to string request)


Here is my function for building a request to send to a server. This code works perfectly fine when it's a small, plain-text file. However, when I pass it a binary file, it crashes on the line indicated below (when it tries to convert that to a utf string and blows up b/c the conversion does not work, as it creates a nil value):

func buildFilePushRequest(fromUrl url: URL, httpBody: Data? = nil, fileName: String) -> NSMutableURLRequest? {
    let cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData
    let request = NSMutableURLRequest(url: url, cachePolicy: cachePolicy, timeoutInterval: 2.0)
    request.httpMethod = "POST"

    // Set Content-Type in HTTP header.
    let boundaryConstant = "Boundary-7MA4YWxkTLLu0UIW"; // This should be auto-generated.
    let contentType = "multipart/form-data; boundary=" + boundaryConstant

    let fileName = fileName

    request.setValue(contentType, forHTTPHeaderField: "Content-Type")
    request.setValue("keep-alive", forHTTPHeaderField: "Connection")

    var dataString = "--\(boundaryConstant)\r\n"
    dataString += "Content-Disposition: form-data; name=file; filename=\"\(fileName)\"\r\n"
    dataString += "Content-Type: octet-stream\r\n\r\n"
    dataString += String(data: httpBody!, encoding: .utf8)! <-- CRASHES HERE
    dataString += "\r\n"
    dataString += "--\(boundaryConstant)--\r\n"

    print("dataString: \(dataString)")

    let requestBodyData = dataString.data(using: .utf8)
    request.httpBody = requestBodyData

    return request
}

I've read a bit that base64 is a better way to go rather than utf8 for binary type data, but I'm not really sure how best to modify the code above to use that instead, or if there is a better solution? Thanks in advance!


Solution

  • If the httpBody parameter can contain binary data you could compound the entire http body as Data

    First of all please use native URLRequest, it's mutable as variable and why is the return value optional?

    func buildFilePushRequest(fromUrl url: URL, httpBody: Data? = nil, fileName: String) -> URLRequest {
        var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 2.0)
    
    ...
    
    var data = Data("--\(boundaryConstant)\r\n".utf8)
    data += Data("Content-Disposition: form-data; name=file; filename=\"\(fileName)\"\r\n".utf8)
    data += Data("Content-Type: octet-stream\r\n\r\n".utf8)
    if let body = httpBody { data += body }
    data += Data("\r\n".utf8)
    data += Data("--\(boundaryConstant)--\r\n".utf8)
    
    request.httpBody = data