Moya: using .uploadCompositeMultipart(multipartData, urlParameters: params) get 404 error

Created on 11 Oct 2017  路  15Comments  路  Source: Moya/Moya

Moya Version: 8.0.5
ruby for CocoaPods 1.3.1


import Moya
enum ServiceRouter {
    case photo(photo: Photo, photoName: String, photoAsset: Data)
}

extension ServiceRouter: TargetType { 
    var path: String {
        switch self {      
        case .photo:
            return BZNetwork.getPathWithClientId(api: "photo")
        }
    }

    var method: Moya.Method {
        switch self {
        case
        .photo:
            return .post
        }
    }
    var headers: [String: String]? {
        return nil
    }

    var task: Task {
        var params =  [String:Any]()
        let uuid = BZDevice.uuid()
        guard let token = AppUtils.getAccessToken() else {
            return .requestPlain
        }
        params["uuid"] = uuid
        params["token"] = token

        switch self {
        case .photo(let photo, let photoName, let asset):

            params["company_id"] = photo.company_id

            let imageData = MultipartFormData(provider: .data(asset), name: "photo", fileName: photoName, mimeType: "image/jpg")

            let multipartData = [imageData]

            return .uploadCompositeMultipart(multipartData, urlParameters: params)

        default:
            break

        }

        return .requestParameters(parameters: params, encoding: URLEncoding.default)

    }


}

question stale

Most helpful comment

I'm dying because Lukasz is answering the questions and Bas is getting all the pings 馃ぃ 馃ぃ

All 15 comments

Hey @vincentSuwenliang, it would really help us if you could provide more details about the problem. Additionally, please take a look at our tutorial about using upload, here - maybe it will help you with your current problem.

@BasThomas Thanks for your reply. I have go through the tutorial, followed the instructions, but when upload image using uploadCompositeMultipart failed. Previously using the old version of Moya using var parameters: [String:Any]? { } + var task: Task {} succeed.

@vincentSuwenliang I suppose you want body parameters then, not urlParameters. Please take a look at this section here. (.uploadMultipart(multipartData) instead of .uploadCompositeMultipart(multipartData, urlParameters: urlParameters)). If you still have problems and you are sure you were using URLEncoding for parameters, I would need your old Moya configuration (TargetType + Provider).

@BasThomas now i have 1. var params = [String:Any]() params["uuid"] = uuid params["token"] = token 2. let imageFormData = MultipartFormData(provider: .data(asset), name: "photo", fileName: photoName, mimeType: "image/jpg") i just tried transform dict to data using let paramsData = NSKeyedArchiver.archivedData(withRootObject: params) let paramsFormData = MultipartFormData(provider: .data(paramsData), name: "description") and then return .uploadMultipart([imageFormData, paramsFormData]) but still get the same error 404. Do you have any clues? thanks for your help

@BasThomas Oh sorry, now get 500 error. i think the backend do not handle the "description" key. But in the old version, I could just using dictionary + data way to upload image + params

@vincentSuwenliang please try the following:

let uuidData = MultipartFormData(provider: .data(uuid.data(using: .utf8)!), name: "uuid")
let tokenData = MultipartFormData(provider: .data(token.data(using: .utf8)!), name: "token")
let imageData = MultipartFormData(provider: .data(asset), name: "photo", fileName: photoName, mimeType: "image/jpg")

let multipartData = [imageData, uuidData, tokenData]

return .uploadMultipart(multipartData)

This one assumes you want to send an image + 2 additional parameters (uuid and token), which you have under uuid and token variables.

@BasThomas in my case, i have almost 10 parameters with the image, and there are int and bool type that need to transform to data type . And need to transfrom every parameter to data type seems would be more tedious than previous params dict + data way.

I'm dying because Lukasz is answering the questions and Bas is getting all the pings 馃ぃ 馃ぃ

@vincentSuwenliang you might be right, we may want to add some form of a convenience method that could do that for us. Although, what we wanted to do, is to unify how we add parameters to multipart upload, because in older version you could use two ways and one of them wasn't obvious/clear - thus the change. Would you be up for a PR? Our code that was doing the convert contained:

if let parameters = target.parameters {
    parameters
        .flatMap { key, value in multipartQueryComponents(key, value) }
        .forEach { key, value in
            if let data = value.data(using: .utf8, allowLossyConversion: false) {
                form.append(data, withName: key)
            }
    }
}

and

/// Encode parameters for multipart/form-data
private func multipartQueryComponents(_ key: String, _ value: Any) -> [(String, String)] {
    var components: [(String, String)] = []

    if let dictionary = value as? [String: Any] {
        for (nestedKey, value) in dictionary {
            components += multipartQueryComponents("\(key)[\(nestedKey)]", value)
        }
    } else if let array = value as? [Any] {
        for value in array {
            components += multipartQueryComponents("\(key)[]", value)
        }
    } else {
        components.append((key, "\(value)"))
    }

    return components
}

@sunshinejr Yeah, understand and thanks for your work. Previously, using var parameters: [String:Any]? { } + var task: Task {} also seems a little bit weird. And sorry that i'm not clear what could i do, you mean create a PR without submitting any codes?

Oh, sorry if I wasn't clear enough. I meant that we could have a convenience method that could convert parameters to multipartFormData in Moya codebase. I'm not sure what would be the best way to handle it (maybe method, maybe MultipartFormData.init or something like that). I asked if you want to take a look what would be the best way to do that, then implement it and make a pull request with the code you've written. This way you could contribute to Moya :)

Of course if you don't feel comfortable with it we can make an issue and maybe somebody else would like to do it. No pressure!

This issue has been marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

This issue has been auto-closed because there hasn't been any activity for at least 21 days. However, we really appreciate your contribution, so thank you for that! 馃檹 Also, feel free to open a new issue if you still experience this problem 馃憤.

@sunshinejr hello, for uploading photo and params through body. For int, i using Data(bytes: &intValue, count: MemoryLayout.size(ofValue: intValue)) trans int to data failed. Then firstly trans int to string, then using stringValue.data(using: .utf8) get the right value. just wanna confirm is this the right practice(all params trans to string type, then trans string to data for uploading)?

@vincentSuwenliang sorry for the delayed answer, was drowning in notifications and just catching up with them. Generally speaking - yes. We were using similar approach before, first make a dictionary ([String: String]) with parameters, then append to the request (these two snippets I posted above).

Let me know if there is anything else I can help you with.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

GurpalRajput picture GurpalRajput  路  3Comments

hamada147 picture hamada147  路  3Comments

fenixsolorzano picture fenixsolorzano  路  3Comments

geraldeersteling picture geraldeersteling  路  3Comments

PlutusCat picture PlutusCat  路  3Comments