2015-04-30 12 views
8

[ATTENZIONE]
Come sto vedendo che la questione è farsi notare più di quanto dovrebbe, voglio dirti di non utilizzare nessuna delle codice seguente
All'epoca in cui ponevo la domanda, Swift aveva meno di un anno, si muoveva velocemente, la maggior parte delle biblioteche non erano veloci e instabili. Si consiglia vivamente di provare a utilizzare Alamofire o un'altra libreria per questo tipo di attività. Ma non farlo da solo.
[/ ATTENZIONE]Caricamento un'immagine e parametri con multipart/form-data a Swift

voglio caricare un'immagine da un endpoint Drupal.

Il problema è che ricevo una risposta HTTP 200 OK con tipo di contenuto text/html. Nella risposta HTML, c'è un chiaro messaggio che il nodo è stato creato correttamente. Ma sul lato server l'immagine non è associata al nodo.

Inoltre non mi aspetto testo/html ma application/json come lo specifico nell'intestazione Accept.

Funziona già nell'app per Android utilizzando Android Rest Template. Ecco il codice di riferimento:

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

So che non controllo la risposta in Android (Void.class), ma tutto funziona bene e l'immagine è allegata al nodo sul lato server.

Ora su iOS in Swift ho provato più cose.

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

manualmente con le informazioni afferrato da altre domande 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("\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)") 
} 

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

Per favore salvami! ;-) Ho provato così tante cose per farlo funzionare che non riesco a vedere più se sto facendo qualcosa di sbagliato. Sembra ok per me. L'unica differenza che posso vedere è che non sto memorizzando l'immagine sul filesystem ma inviando direttamente i dati binari che è la stessa cosa alla fine.

Ecco un'immagine della richiesta creata in Postman (di lavoro e ricevere JSON)

Postman

[EDIT] se può aiutare qualcuno qui è il codice corretto della parte sbagliata della sopra richiesta manuale:

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 
+0

come è questo fatto quando ho bisogno di inviare i parametri di post pure? http://stackoverflow.com/questions/30006290/uploading-image-in-swift-with-multiple-parameters – Tyler

+0

Perché si specifica 'image.jpeg' se il file ha l'estensione' png'? –

+0

Inoltre, mi chiedo sempre quali siano i due trattini aggiuntivi prima della stringa limite ('--'). –

risposta

2

non so se questo funzionerà per quello che si sta cercando di fare, ma usiamo questo per caricare le immagini, La differenza chiave è quella di utilizzare filename e 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]; 
+1

OMG. Grazie mille. Mi hai messo sulla strada giusta. Non so perché ho fatto così schifo con la richiesta "manuale" ma se guardi alla mia domanda è completamente sbagliato (nessun limite finale ecc.) E in più ... il corpo non è impostato sulla richiesta . L'aggiunta di Content-Type image/jpeg alla fine mi ha dato un errore dal server invece del HTTP 200 errato. Ciò che mi dà fastidio è che AFNetworking e SRWebClient non hanno funzionato anche se sono molto utilizzati. – florian

+0

Fantastico, felice che tu abbia funzionato! Sì, il networking sembra sempre più doloroso di quanto dovrebbe essere. – rmp

1

Per qualsiasi rapido 2.0 JSON richiesta e PHP Codice: - (Manuale) Codice

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}”; 

} 

?> 
Problemi correlati