Téléchargez une image et des paramètres de type multipart/form-data en Swift

[AVERTISSEMENT]

Comme je vois que cette question est de se faire remarquer de plus que l',
Je tiens à vous dire de ne pas utiliser le code suivant.

Au moment où j'ai posé la question, Swift a moins d'un an, était en mouvement rapide, la plupart des bibliothèques n'ont pas été Swift-friendly et instable.
Je vous recommande fortement d'essayer en utilisant Alamofire ou d'une autre bibliothèque pour ce genre de tâche. Mais ne le faites pas vous-même.

[/WARNING]

Je veux télécharger une image sur un Drupal point de terminaison.

Le problème que j'ai est que je reçois une réponse HTTP 200 OK avec le texte/html type de contenu. Dans le code HTML de la réponse, il y a un message clair que le nœud a été correctement créé. Mais du côté serveur, l'image n'est pas associée au nœud.

Aussi je ne m'attends pas à du texte/html mais l'application/json comme je l'ai indiquer dans l'en-tête Accept.

Il travaille déjà dans l'application Android en utilisant Android Reste Modèle. Voici le code de référence:

String url = getUrl("node/{info_id}/attach_file");

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);

if (user.isLoggedIn()) {
    headers.add(user.getSessionName(), user.getSessionId());
    headers.add("X-CSRF-Token", user.getToken());
    headers.add("Cookie", user.getSessionName() + "=" + user.getSessionId());
}

MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();

parts.add("files[field_mobileinfo_image]",
        new FileSystemResource(info.getImageUri()));
parts.add("field_name", "field_mobileinfo_image");

HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(parts, headers);
return getRestTemplate().exchange(url, HttpMethod.POST, request, Void.class, info.getId()).getBody();

Je sais que je ne cochez pas la réponse dans Android (Void.class) mais tout fonctionne très bien et l'image est attaché le noeud sur le côté serveur.

Maintenant sur iOS dans Swift, j'ai essayé plusieurs choses.

Avec AFNetworking:

func upload(mobileInfo: MobileInfo) {
    let user = userService.load()
    let url = Config.buildUrl("")

    let manager = AFHTTPRequestOperationManager(baseURL: NSURL(string:url)!)
    let serializer = AFHTTPRequestSerializer()
    serializer.setValue(user.sessionId, forHTTPHeaderField: user.sessionName)
    serializer.setValue(user.token, forHTTPHeaderField: "X-CSRF-Token")
    serializer.setValue("\(user.sessionName)=\(user.sessionId)", forHTTPHeaderField: "Cookie")
    manager.requestSerializer = serializer

    manager.responseSerializer.acceptableContentTypes.removeAll(keepCapacity: false)
    manager.responseSerializer.acceptableContentTypes.insert("application/json")

    let imageData = UIImageJPEGRepresentation(mobileInfo.image, 0.3)    
    manager.POST("/node/\(mobileInfo.id)/attach_file", parameters: nil, constructingBodyWithBlock: { (formData) -> Void in
        formData.appendPartWithFileData(
            imageData,
            name: "files[field_mobileinfo_image]",
            fileName: "field_mobileinfo_image",
            mimeType: "image/jpeg")
        formData.appendPartWithFormData("field_mobileinfo_image".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true), name: "field_name")
    },
    success: { (operation, data) -> Void in
        println(data)
    }) { (operation, error) -> Void in
        println(error)
    }
}

Manuellement avec les informations saisies à partir d'autres stackoverflow questions:

func upload2(mobileInfo: MobileInfo) {
let user = userService.load()
let imageData = UIImageJPEGRepresentation(mobileInfo.image, 0.3)
let url = NSURL(string:Config.buildUrl("/node/\(mobileInfo.id)/attach_file"))!
println(url)
var request = NSMutableURLRequest(URL: url)
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var boundary = "---------------------------14737809831466499882746641449"
var contentType = "multipart/form-data; boundary=\(boundary)"
println(contentType)
request.addValue(contentType, forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("\(user.sessionName)=\(user.sessionId)", forHTTPHeaderField: "Cookie")
request.addValue(user.sessionId, forHTTPHeaderField: user.sessionName)
request.addValue(user.token, forHTTPHeaderField: "X-CSRF-Token")
println(request.allHTTPHeaderFields)
var body = NSMutableData()
body.appendData("\r\n--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; name=\"field_name\"\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("field_mobileinfo_image".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
body.appendData("\r\n--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; name=\"files[field_mobileinfo_image]\"; filename=\"img.jpg\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Type: application/octet-stream\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(imageData)
body.appendData("\r\n--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
var returnData = NSURLConnection.sendSynchronousRequest(request, returningResponse: nil, error: nil)
var returnString = NSString(data: returnData!, encoding: NSUTF8StringEncoding)
println("returnString \(returnString)")
}

Avec SRWebClient:

func upload3(mobileInfo: MobileInfo) {
let user = userService.load()
let imageData:NSData = NSData(data: UIImageJPEGRepresentation(mobileInfo.image, 0.3))
SRWebClient.POST("http://master.test.lesfrontaliers.lu/node/\(mobileInfo.id)/attach_file")
.headers(["Accept": "application/json",
user.sessionName: user.sessionId,
"X-CSRF-Token": user.token,
"Cookie": "\(user.sessionName)=\(user.sessionId)"])
.data(imageData, fieldName:"files[field_mobileinfo_image]", data:["field_name":"field_mobileinfo_image"])
.send({ (response: AnyObject!, status: Int) -> Void in
println(status)
println(response)
},failure:{(error:NSError!) -> Void in
println(error)
})
}

Sauvez-moi! 😉
J'ai essayé beaucoup de choses pour le faire fonctionner, que je ne peux pas voir plus si je fais quelque chose de mal. Il semble ok pour moi. La seule différence que je peux voir, c'est que je ne suis pas stocker l'image sur le système de fichiers, mais directement à partir de l'envoi de données binaires qui est la même chose à la fin.

Voici une image de la demande créée en Facteur (de travail et de réception json)

Téléchargez une image et des paramètres de type multipart/form-data en Swift

[EDIT] Si cela peut aider quelqu'un voici le bon code de la mauvaise partie de la ci-dessus manuel de la demande:

var body = NSMutableData()
body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; name=\"field_name\"\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("field_mobileinfo_image\r\n".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; name=\"files[field_mobileinfo_image]\"; filename=\"img.jpg\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Type: image/jpeg\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(imageData)
body.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("--\(boundary)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
request.HTTPBody = body
comment est-ce fait quand j'en ai besoin pour envoyer des paramètres post? stackoverflow.com/questions/30006290/...
Pourquoi avez-vous spécifier image.jpeg si votre fichier comporte l'extension png?
Aussi, je me demande toujours quelles sont les deux autres principaux traits d'union avant la limite de chaîne (--).
C'était un travail en cours de code parce que je savais que j'étais l'envoi de fichier jpeg et mon souci est d'envoyer un multipart formulaire de demande, de ne pas savoir ce que j'étais vraiment envoyer. Les deux traits d'union ne sont qu'une partie de la spec, je pense. Chacun --frontière est de la partie et la dernière partie est fermé-limite-- RFC

OriginalL'auteur florian | 2015-04-30