Загрузка изображения и параметров с помощью multipart / form-data в Swift

[предупреждение]
Поскольку я вижу, что этот вопрос замечается больше, чем должен, Я хочу сказать вам, чтобы вы не использовали какой-либо из следующих кодов.
В то время, когда я задал вопрос, у Свифта было меньше года, он двигался быстро, большинство библиотек не были быстрыми и нестабильными. Я настоятельно рекомендую вам попробовать использовать Alamofire или другая библиотека для такого рода задач. Но не делайте этого сами.
[/внимание]

Я хочу загрузить изображение в конечную точку Drupal.

проблема в том, что я получаю ответ HTTP 200 OK с типом содержимого text/html. В ответе HTML есть четкое сообщение о том, что узел был правильно создан. Но на стороне сервера образ не связан с узлом.

также я не ожидаю text / html, но application / json, поскольку я указываю его в заголовке Accept.

он уже работает в приложении для Android Использование шаблона Android Rest. Вот код для справки:

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();

Я знаю, что не проверяю ответ в Android (Void.class) но все работает нормально, и изображение прикреплено к узлу на стороне сервера.

теперь на iOS в Swift я пробовал несколько вещей.

С 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)
    }
}

вручную С информацией, захваченной из других вопросов stackoverflow:

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("rn--(boundary)rn".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("Content-Disposition: form-data; name="field_name"rnrn".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("field_mobileinfo_image".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)

    body.appendData("rn--(boundary)rn".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("Content-Disposition: form-data; name="files[field_mobileinfo_image]"; filename="img.jpg"rn".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("Content-Type: application/octet-streamrnrn".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData(imageData)
    body.appendData("rn--(boundary)rn".dataUsingEncoding(NSUTF8StringEncoding)!)


    var returnData = NSURLConnection.sendSynchronousRequest(request, returningResponse: nil, error: nil)

    var returnString = NSString(data: returnData!, encoding: NSUTF8StringEncoding)

    println("returnString (returnString)")
}

С 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)
        })
}

Пожалуйста, спаси меня! ;-) Я пробовал так много вещей, чтобы заставить его работать, что я больше не вижу, если я делаю что-то неправильно. Для меня это нормально. Единственное отличие, которое я вижу, заключается в том, что я не сохраняю изображение в файловой системе, а напрямую отправляю двоичные данные, что в конце концов одно и то же.

вот изображение запроса, созданного в Postman (работа и получение в JSON)

Postman

[EDIT] если это может помочь кому-то, вот правильный код неправильной части приведенного выше ручного запроса:

var body = NSMutableData()

body.appendData("--(boundary)rn".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; name="field_name"rnrn".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("field_mobileinfo_imagern".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)

body.appendData("--(boundary)rn".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; name="files[field_mobileinfo_image]"; filename="img.jpg"rn".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Type: image/jpegrnrn".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(imageData)
body.appendData("rn".dataUsingEncoding(NSUTF8StringEncoding)!)

body.appendData("--(boundary)--rn".dataUsingEncoding(NSUTF8StringEncoding)!)

request.HTTPBody = body

2 ответов


не знаю, будет ли это работать для того, что вы пытаетесь сделать, но мы используем это для загрузки изображений, ключевое отличие-использовать filename и Content-type:

[self appendBody:body data:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\".jpg\"\r\n", key]];

[self appendBody:body data:@"Content-Type: image/jpeg\r\n\r\n"];
[body appendData:imageData];

для любого запроса swift 2.0 JSON и PHP-кода: - (Manual )

    let imageData = UIImageJPEGRepresentation(userImage, 0.3)
    let url:NSURL = NSURL(string: serverURL!)! // Give ur request URL
    let request = NSMutableURLRequest(URL: url)
    request.HTTPMethod = "POST"
    let boundary = "---------------------------14737809831466499882746641449"
    let contentType = "multipart/form-data; boundary=\(boundary)"
    request.addValue(contentType, forHTTPHeaderField: "Content-Type")
    let body = NSMutableData()
    body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("Content-Disposition: form-data; name=\"userfile\"; filename=\"img.jpg\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("Content-Type: image/jpeg\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("Content-Transfer-Encoding: binary\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

PHP код :-

<?php

//http://192.168.1.154/Contact/uploadImgwebservice.php

//print the username  and password using php

echo $_POST[‘username’];

echo $_POST[‘password’];

//upload your file

$uploaddir = ‘./uploads/’;

$file = basename($_FILES[‘userfile’][‘name’]);

$uploadfile = $uploaddir . $file;

if (move_uploaded_file($_FILES[‘userfile’][‘tmp_name’], $uploadfile)) {

    echo “http://192.168.1.154/Contact/uploads/{$file}”;

}

?>