devxoul / MoyaSugar

🍯 Syntactic sugar for Moya
MIT License
192 stars 30 forks source link

Upload image with MoyaSugar #21

Closed dtomicAZ closed 6 years ago

dtomicAZ commented 7 years ago

Hi,

Can someone give me example to upload image with MoyaSugar, thank you.

hitman1711 commented 7 years ago

Hi. Look at this

import Moya
import MoyaSugar

enum MyPhotoApi: SugarTargetType {
    case uploadPhoto(photo: UIImage)
    case deletePhoto(photoId: Int)

    var baseURL: URL {
        return URL(string: Consts.URI.Api.v1)!
    }

    var route: Route {
        switch self {
        case .uploadPhoto:
            return .post("/service/photo")
        case let .deletePhoto(photoId):
            return .delete("/service/photo/\(photoId)")
        }
    }

    var params: Parameters? { return nil }

    var task: Task {
        switch self {
        case let .addPhoto(_, photo):
            return Task.upload(.multipart(self.multipartData(image: photo)))
        case let .uploadPhoto(photo):
            return Task.upload(.multipart(self.multipartData(image: photo)))
        default:
            return .request
        }
    } 
    private func multipartData(image: UIImage) -> [MultipartFormData] {
        guard let data = UIImageJPEGRepresentation(image, 0.6) else { return [MultipartFormData]() }
        return [MultipartFormData(provider: .data(data), name: "image", fileName: "image.jpeg", mimeType:"image/jpeg")]
    }  
    var httpHeaderFields: [String: String]? { return nil } 
    var sampleData: Data { return Data() }
}
dtomicAZ commented 7 years ago

Hi @hitman1711

Thank you for your help.

I have one more issue with image upload:

Backend guy made api like this (for image upload): POST "UserProfile/UploadCarImage?carId={carId}"

So I have in my code: case .addCarImagee(let carId,_): return .post("UserProfile/UploadCarImage?carId=(carId)")

When I fire request, in Moya log I see: http://xxxx/UserProfile/UploadCarImage%3FcarId=243

And I get error from server.

With Postman all is working fine, probably because with postman url is not encoded, and with moya it is.

What can I do to fix this, thank you.

hitman1711 commented 7 years ago

@dtomicAZ you should not explicitly write parameters in url. All you need is implement "params" computed property by applying a URLEncoding on a parameters dictionary. (There is also JSONEncoding for another purposes.) Finally it looks like this:

var route: Route {
     switch self { case .addCarImagee: return .post("UserProfile/UploadCarImage") }
}

var params: Parameters? {
        switch self {
        case let .addCarImagee(carId, _):
            return URLEncoding() => ["carId" : carId]
        default: return nil
        }
}
dtomicAZ commented 7 years ago

@hitman1711 I tried that, but then carId parameter goes in body, and I get 404 from server.

Is this backend guy mistake, because he expects parameter in url instead of body ?

hitman1711 commented 7 years ago

@dtomicAZ i'm convinced that URLEncoding doesn't encode parameters in body, instead it will generate url-encoded query string which will be appended at the end of request path. URLEncoding() => ["carId" : carId] will produce "?carId={carId}"

May be you have used JSONEncoding? Please check this and write me back)

dtomicAZ commented 7 years ago

@hitman1711 I checked and it indeed goes in body instead in url: Here is all information:

enum MyApi {

case addCarImagee(carId: Int, image: UIImage) }

extension MyApi : SugarTargetType {

var baseURL: URL { return URL(string: "my.host.url")! }

var route: Route { switch self { case .addCarImagee(,): return .post("UserProfile/UploadCarImage") }

var params: Parameters? { switch self { case .addCarImagee(let carId,_): return URLEncoding() => ["carId" : carId] }

var task: Task { switch self { case let .addCarImagee(_, photo): return Task.upload(.multipart(self.multipartData(image: photo))) default: return .request } }

private func multipartData(image: UIImage) -> [MultipartFormData] { guard let data = UIImageJPEGRepresentation(image, 0.8) else { return [MultipartFormData]() } return [MultipartFormData(provider: .data(data), name: "", fileName: "image.jpeg", mimeType:"image/jpeg")] }

}

This is log output: Moya_Logger: [19/09/2017 12:31:55] Request: http://my.host.url/UserProfile/UploadCarImage Moya_Logger: [19/09/2017 12:31:55] Request Headers: ["Content-Type": "multipart/form-data; boundary=alamofire.boundary.75e2fe468cd4d21a", "Accept-Language": "hr", "Authorization": "some_token", "Accept": "application/json"] Moya_Logger: [19/09/2017 12:31:55] HTTP Request Method: POST Moya_Logger: [19/09/2017 12:31:55] Request Body: carId=248

dtomicAZ commented 7 years ago

@hitman1711 Hi, any ideas how to resolve above issue ?

dtomicAZ commented 7 years ago

@hitman1711 I it looks llike Moya 9 solves this problem with this: requestCompositeParameters(bodyParameters:bodyEncoding:urlParameters:)

Can I use Moja-Sugar with Moya 9 ?

devxoul commented 7 years ago

@dtomicAZ, Of course! From 1.0.0 we support Moya 9 :tada: But sadly I'm not able to push the podspec to CocoaPods trunk because of https://github.com/Moya/Moya/pull/1306 :cry:

hitman1711 commented 7 years ago

@dtomicAZ Looks like a bug when combining Parameters with Task.upload.

dtomicAZ commented 6 years ago

I solved this using MoyaSugar 1.1 and Moya 10.0:

  var task: Task {
    switch self {
    case let .addCarImage(carId, image):
      return Task.uploadCompositeMultipart(self.multipartData(image: image), urlParameters: ["carId": carId])
    default:
      guard let parameters = self.parameters else { return .requestPlain }
      return .requestParameters(parameters: parameters.values, encoding: parameters.encoding)
    }
  }

  private func multipartData(image: UIImage) -> [Moya.MultipartFormData] {
    guard let data = UIImageJPEGRepresentation(image, 1.0) else { return [Moya.MultipartFormData]() }
    return [MultipartFormData(provider: .data(data), name: "", fileName: "image.jpeg", mimeType:"image/jpeg")]
  }