Subiendo archivo con parámetros usando Alamofire


95

Estoy intentando cargar un archivo usando Alamofire. La carga funciona bien cuando se usa un archivo ( NSUrl), sin embargo, parece que no puedo entender cómo usar la NSDataopción.

Esto es lo que tengo como prueba:

 var url:NSURL = NSURL.URLWithString("http://localhost:8080/bike.jpeg")

 var err: NSError?
 var imageData :NSData = NSData.dataWithContentsOfURL(url,options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err)

 Alamofire.upload(.POST, "http://localhost:8080/rest/service/upload/test.png", imageData)
        .progress { (bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) in
            println(totalBytesWritten)
        }
        .responseJSON { (request, response, JSON, error) in
            println(request)
            println(response)
           println(JSON)
 }

¿Recibo un código de estado 415?

Además, ¿cómo puedo enviar parámetros adicionales en la carga?

Gracias

EDITAR

No estaba configurando el tipo de contenido correcto:

var manager = Manager.sharedInstance
manager.session.configuration.HTTPAdditionalHeaders = ["Content-Type": "application/octet-stream"]


let imageData: NSMutableData = NSMutableData.dataWithData(UIImageJPEGRepresentation(imageTest.image, 30));

Alamofire.upload(.POST, "http://localhost:8080/rest/service/upload?attachmentName=file.jpg",  imageData)
        .progress { (bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) in
            println(totalBytesWritten)
        }
        .responseString { (request, response, JSON, error) in
            println(request)
            println(response)
            println(JSON)
}

Todavía no puedo averiguar cómo enviar parámetros adicionales junto con la carga.


Utilice "multipartFormData.appendBodyPart (data: image1Data, name:" file ", fileName:" myImage.png ", mimeType:" image / png ")" esto es imp; de lo contrario, aparece el error "Valor no válido alrededor del carácter 0"
Avijit Nagare

Respuestas:


81

Aquí hay una función simple que requiere la URL de carga de destino, los parámetros y los datos de imagen y devuelve URLRequestConvertible y NSData que Alamofire.upload requiere para cargar una imagen con parámetros.

// this function creates the required URLRequestConvertible and NSData we need to use Alamofire.upload
func urlRequestWithComponents(urlString:String, parameters:Dictionary<String, String>, imageData:NSData) -> (URLRequestConvertible, NSData) {

    // create url request to send
    var mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: urlString)!)
    mutableURLRequest.HTTPMethod = Alamofire.Method.POST.rawValue
    let boundaryConstant = "myRandomBoundary12345";
    let contentType = "multipart/form-data;boundary="+boundaryConstant
    mutableURLRequest.setValue(contentType, forHTTPHeaderField: "Content-Type")



    // create upload data to send
    let uploadData = NSMutableData()

    // add image
    uploadData.appendData("\r\n--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    uploadData.appendData("Content-Disposition: form-data; name=\"file\"; filename=\"file.png\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    uploadData.appendData("Content-Type: image/png\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    uploadData.appendData(imageData)

    // add parameters
    for (key, value) in parameters {
        uploadData.appendData("\r\n--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
        uploadData.appendData("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n\(value)".dataUsingEncoding(NSUTF8StringEncoding)!)
    }
    uploadData.appendData("\r\n--\(boundaryConstant)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)



    // return URLRequestConvertible and NSData
    return (Alamofire.ParameterEncoding.URL.encode(mutableURLRequest, parameters: nil).0, uploadData)
}    

A continuación, se muestra un ejemplo de cómo utilizarlo (consulte CREAR Y ENVIAR UNA SOLICITUD):

// init paramters Dictionary
var parameters = [
    "task": "task",
    "variable1": "var"
]

// add addtionial parameters
parameters["userId"] = "27"
parameters["body"] = "This is the body text."

// example image data
let image = UIImage(named: "177143.jpg")
let imageData = UIImagePNGRepresentation(image)



// CREATE AND SEND REQUEST ----------

let urlRequest = urlRequestWithComponents("http://example.com/uploadText/", parameters: parameters, imageData: imageData)

Alamofire.upload(urlRequest.0, urlRequest.1)
    .progress { (bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) in
        println("\(totalBytesWritten) / \(totalBytesExpectedToWrite)")
    }
    .responseJSON { (request, response, JSON, error) in
        println("REQUEST \(request)")
        println("RESPONSE \(response)")
        println("JSON \(JSON)")
        println("ERROR \(error)")
}    

Y si necesita el archivo php para la URL de destino (con una carpeta de 'cargas' en el mismo directorio):

// get picture variables
$file       = $_FILES['file']['tmp_name'];
$fileName   = $_FILES['file']['name'];
$fileType   = $_FILES['file']['type'];

// check extension
$allowedExts = array("jpg", "jpeg", "png");
$rootName = reset(explode(".", $fileName));
$extension = end(explode(".", $fileName));

// create new file name
$time = time();
$newName = $rootName.$time.'.'.$extension;

// temporarily save file
$moved = move_uploaded_file($_FILES["file"]["tmp_name"], "uploads/".$newName );
if ($moved) $path = "uploads/".$newName;

$body = $_POST['body'];
$userId = $_POST['userId'];


$time = time();
if ($moved) {
    $fullUrl = "http://antiblank.com/testPhotoUpload/".$path;
    $arrayToSend = array('status'=>'success','time'=>$time,'body'=>$body,'userId'=>$userId, "imageURL"=>$fullUrl);
} else {
    $arrayToSend = array('status'=>'FAILED','time'=>$time,'body'=>$body,'userId'=>$userId);
}

header('Content-Type:application/json');
echo json_encode($arrayToSend);

Hola, ya uso este método y funciona bien. Pero en el lado del servidor cambian el tipo de contenido de la solicitud JSON. Después de eso, este método no funciona. Quiero cargar datos como json ... ¿pueden ayudarme por favor?
mychar

1
casi a la derecha, uploadData.appendData ("Content-Disposition: form-data; name = \" file \ "; filename = \" file.png \ "\ r \ n" .dataUsingEncoding (NSUTF8StringEncoding)!) en esta línea name = \ "archivo \", de hecho el archivo es el nombre de la clave que tratará el servidor ... como un clip.
Albert.Qing

Mines estaba funcionando antes y ahora obtengo el código 3840 de NSCocoaErrorDomain, ¿alguien ha resuelto este problema?
Padrino

@antiblank gracias por compartir este código. ¿Podría aconsejarme cómo modificar esta función para que pueda aceptar NSData o nulo para el parámetro de imagen, ya que los usuarios tienen la opción de cargar o no una imagen en mi llamada de servicio web? ¿Podría usar anyObject en lugar de NSData en la definición
User2363025

3
¿Cómo podemos hacer esto incluyendo también los encabezados?
Poonam

73

Cargue fotos / archivos con parámetros y encabezados personalizados a través de Swift 3 y 4 y Alamofire 4

// import Alamofire
func uploadWithAlamofire() {
  let image = UIImage(named: "bodrum")!

  // define parameters
  let parameters = [
    "hometown": "yalikavak",
    "living": "istanbul"
  ]

  Alamofire.upload(multipartFormData: { multipartFormData in
    if let imageData = UIImageJPEGRepresentation(image, 1) {
      multipartFormData.append(imageData, withName: "file", fileName: "file.png", mimeType: "image/png")
    }

    for (key, value) in parameters {
      multipartFormData.append((value?.data(using: .utf8))!, withName: key)
    }}, to: "upload_url", method: .post, headers: ["Authorization": "auth_token"],
        encodingCompletion: { encodingResult in
          switch encodingResult {
          case .success(let upload, _, _):
            upload.response { [weak self] response in
              guard let strongSelf = self else {
                return
              }
              debugPrint(response)
            }
          case .failure(let encodingError):
            print("error:\(encodingError)")
          }
  })
}

vía Swift 2 y Alamofire 3

  // import Alamofire
  func uploadWithAlamofire() {
    let image = UIImage(named: "myImage")!

    // define parameters
    let parameters = [
      "hometown": "yalikavak",
      "living": "istanbul"
    ]

    // Begin upload
    Alamofire.upload(.POST, "upload_url",
      // define your headers here
      headers: ["Authorization": "auth_token"],
      multipartFormData: { multipartFormData in

        // import image to request
        if let imageData = UIImageJPEGRepresentation(image, 1) {
          multipartFormData.appendBodyPart(data: imageData, name: "file", fileName: "myImage.png", mimeType: "image/png")
        }

        // import parameters
        for (key, value) in parameters {
          multipartFormData.appendBodyPart(data: value.dataUsingEncoding(NSUTF8StringEncoding)!, name: key)
        }
      }, // you can customise Threshold if you wish. This is the alamofire's default value
      encodingMemoryThreshold: Manager.MultipartFormDataEncodingMemoryThreshold,
      encodingCompletion: { encodingResult in
        switch encodingResult {
        case .Success(let upload, _, _):
          upload.responseJSON { response in
            debugPrint(response)
          }
        case .Failure(let encodingError):
          print(encodingError)
        }
    })
  }

Versión rápida actual: https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#uploading-data-to-a-server


¿Puede alguien decirme acerca de estas líneas a continuación si deja imageData = UIImageJPEGRepresentation (image, 1) {multipartFormData.append (imageData, withName: "file", fileName: "file.png", mimeType: "image / png")} para (clave, valor) en los parámetros {multipartFormData.append ((value? .data (using: .utf8)) !, withName: key)}}
Ravi Ojha

1
Esto no funcionará si parameterses [String:Any]porque value.data(using: .utf8))!no es válido para Any Type. ¿Tiene alguna sugerencia de cómo resolver esto?
Chlebta

57

Aquí hay una solución que usa Alamofire 3.0 basada en la respuesta de antiblanks:

 let parameters = [
            "par1": "value",
            "par2": "value2"]    

 let URL = "YOUR_URL.php"

 let image = UIImage(named: "image.png")

 Alamofire.upload(.POST, URL, multipartFormData: {
                multipartFormData in

                if let _image = image {
                    if let imageData = UIImageJPEGRepresentation(_image, 0.5) {
                        multipartFormData.appendBodyPart(data: imageData, name: "file", fileName: "file.png", mimeType: "image/png")
                    }
                }

                for (key, value) in parameters {
                    multipartFormData.appendBodyPart(data: value.dataUsingEncoding(NSUTF8StringEncoding)!, name: key)
                }

            }, encodingCompletion: {
                encodingResult in

                switch encodingResult {
                case .Success(let upload, _, _):
                     upload.responseObject { (response: Response<UploadData, NSError>) -> Void in

                     switch response.result {
                     case .Success:
                         completionHandler?(success: true)
                     case .Failure(let error):
                         completionHandler?(success: false)
                     }

                 }
                case .Failure(let encodingError):
                    print(encodingError)
                }
        })

¿Cómo cancelarías esta carga? En otras respuestas, veo personas que dicen que asuman la carga a una var, pero cuando lo hago, el tipo se infiere como (), por lo que no permite que se invoquen métodos. Gracias.
Sean Lintern

@ SeanLintern88: Puede llamar a cancel () en el objeto de solicitud. Algo como: upload.cancel (). Puede asignar el objeto de solicitud a una variable y llamar a cancel ()
EdFunke

1
case .Success(let upload, _, _) uploadno puede encontrar. ¿Me estoy perdiendo de algo?
fatihyildizhan

@fatihyildizhan encodingResultes una MultipartFormDataEncodingResultenumeración. El .Success Casetiene los siguientes parámetros: ( request: Request, streamingFromDisk: Bool, streamFileURL: NSURL?) Así que uploades una Solicitud. ¿Agregaste import Alamofire?
EdFunke

¿Cómo sigue progresando con esto?
hris

10

Mejorando la respuesta de EdFunke para Swift 2.2 Alamofire 3.3.1

Alamofire.upload(.POST, urlString, multipartFormData: {
            multipartFormData in
            if let _image = self.profilePic.image {
                if let imageData = UIImagePNGRepresentation(_image) {
                    multipartFormData.appendBodyPart(data: imageData, name: "user_image", fileName: "file.png", mimeType: "image/png")
                }
            }
            for (key, value) in userInfo {
                multipartFormData.appendBodyPart(data: value.dataUsingEncoding(NSUTF8StringEncoding)!, name: key)
            }
            }, encodingCompletion: { encodingResult in
                switch encodingResult {
                case .Success(let upload, _, _):
                    upload.responseJSON { response in
                        debugPrint(response)
                    }
                case .Failure(let encodingError):
                    print(encodingError)
                }
            }
        )

4

La carga de varias partes está programada para incluirse con la próxima versión (1.3.0) de Alamofire. Mientras tanto, utilizando la información de este hilo, he creado una clase que simplifica la carga de archivos e incluye parámetros adicionales ("entradas" regulares) en la solicitud junto con uno o más archivos. Sin asumir que los archivos son de un tipo particular o el uso de Routers.

FileUploader.swift:

import Foundation
import Alamofire

private struct FileUploadInfo {
  var name:String
  var mimeType:String
  var fileName:String
  var url:NSURL?
  var data:NSData?

  init( name: String, withFileURL url: NSURL, withMimeType mimeType: String? = nil ) {
    self.name = name
    self.url = url
    self.fileName = name
    self.mimeType = "application/octet-stream"
    if mimeType != nil {
      self.mimeType = mimeType!
    }
    if let _name = url.lastPathComponent {
      fileName = _name
    }
    if mimeType == nil, let _extension = url.pathExtension {
      switch _extension.lowercaseString {

      case "jpeg", "jpg":
        self.mimeType = "image/jpeg"

      case "png":
        self.mimeType = "image/png"

      default:
        self.mimeType = "application/octet-stream"
      }
    }
  }

  init( name: String, withData data: NSData, withMimeType mimeType: String ) {
    self.name = name
    self.data = data
    self.fileName = name
    self.mimeType = mimeType
  }
}

class FileUploader {

  private var parameters = [String:String]()
  private var files = [FileUploadInfo]()
  private var headers = [String:String]()

  func setValue( value: String, forParameter parameter: String ) {
    parameters[parameter] = value
  }

  func setValue( value: String, forHeader header: String ) {
    headers[header] = value
  }

  func addParametersFrom( #map: [String:String] ) {
    for (key,value) in map {
      parameters[key] = value
    }
  }

  func addHeadersFrom( #map: [String:String] ) {
    for (key,value) in map {
      headers[key] = value
    }
  }

  func addFileURL( url: NSURL, withName name: String, withMimeType mimeType:String? = nil ) {
    files.append( FileUploadInfo( name: name, withFileURL: url, withMimeType: mimeType ) )
  }

  func addFileData( data: NSData, withName name: String, withMimeType mimeType:String = "application/octet-stream" ) {
    files.append( FileUploadInfo( name: name, withData: data, withMimeType: mimeType ) )
  }

  func uploadFile( request sourceRequest: NSURLRequest ) -> Request? {
    var request = sourceRequest.mutableCopy() as! NSMutableURLRequest
    let boundary = "FileUploader-boundary-\(arc4random())-\(arc4random())"
    request.setValue( "multipart/form-data;boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
    let data = NSMutableData()

    for (name, value) in headers {
      request.setValue(value, forHTTPHeaderField: name)
    }

    // Amazon S3 (probably others) wont take parameters after files, so we put them first        
    for (key, value) in parameters {
      data.appendData("\r\n--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
      data.appendData("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n\(value)".dataUsingEncoding(NSUTF8StringEncoding)!)
    }

    for fileUploadInfo in files {
      data.appendData( "\r\n--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)! )
      data.appendData( "Content-Disposition: form-data; name=\"\(fileUploadInfo.name)\"; filename=\"\(fileUploadInfo.fileName)\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
      data.appendData( "Content-Type: \(fileUploadInfo.mimeType)\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
      if fileUploadInfo.data != nil {
        data.appendData( fileUploadInfo.data! )
      }
      else if fileUploadInfo.url != nil, let fileData = NSData(contentsOfURL: fileUploadInfo.url!) {
        data.appendData( fileData )
      }
      else { // ToDo: report error
        return nil
      }
    }

    data.appendData("\r\n--\(boundary)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

    return Alamofire.upload( request, data )
  }

}

Se usaría así:

// This example uploads a file called example.png found in the app resources

let fileURL = NSBundle.mainBundle().URLForResource("example", withExtension: "png")
let fileUploader = FileUploader()
// we can add multiple files
// this would be equivalent to: <input type="file" name="myFile"/>
fileUploader.addFileURL(fileURL!, withName: "myFile")
// we can add NSData objects directly
let data = UIImage(named: "sample")
fileUploader.addFileData( UIImageJPEGRepresentation(data,0.8), withName: "mySecondFile", withMimeType: "image/jpeg" )
// we can also add multiple aditional parameters
// this would be equivalent to: <input type="hidden" name="folderName" value="sample"/>
fileUploader.setValue( "sample", forParameter: "folderName" )
// put your server URL here
var request = NSMutableURLRequest( URL: NSURL(string: "http://myserver.com/uploadFile" )! )
request.HTTPMethod = "POST"
fileUploader.uploadFile(request: request)

Compruébalo o descárgalo de esta esencia: https://gist.github.com/ncerezo/b1991f8dfac01cb162c0


Hola, ¿hay alguna forma de verificar el éxito del proceso de carga o más bien el fracaso en el lado de iOS, en su caso de ejemplo? Gracias por responder.
Zigii Wong

Sí, por supuesto. Como mencioné, el método uploadFile (request) devuelve un objeto Request, al igual que el método Alamofire.request, por lo que simplemente puede encadenar el progreso y / o cierres de respuesta. Por ejemplo: fileUploader.uploadFile (request: request) .response {(request, response, data, error) in ....}
ncerezo

3

El código en la respuesta de @ antiblank no me funcionaba. Hice algunos cambios y ahora está funcionando:

func urlRequestWithComponents(urlString:String, parameters:NSDictionary) -> (URLRequestConvertible, NSData) {

    // create url request to send
    var mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: urlString)!)
    mutableURLRequest.HTTPMethod = Alamofire.Method.POST.rawValue
    //let boundaryConstant = "myRandomBoundary12345"
    let boundaryConstant = "NET-POST-boundary-\(arc4random())-\(arc4random())"
    let contentType = "multipart/form-data;boundary="+boundaryConstant
    mutableURLRequest.setValue(contentType, forHTTPHeaderField: "Content-Type")


    // create upload data to send
    let uploadData = NSMutableData()

    // add parameters
    for (key, value) in parameters {

        uploadData.appendData("\r\n--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

        if value is NetData {
            // add image
            var postData = value as NetData


            //uploadData.appendData("Content-Disposition: form-data; name=\"\(key)\"; filename=\"\(postData.filename)\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

            // append content disposition
            var filenameClause = " filename=\"\(postData.filename)\""
            let contentDispositionString = "Content-Disposition: form-data; name=\"\(key)\";\(filenameClause)\r\n"
            let contentDispositionData = contentDispositionString.dataUsingEncoding(NSUTF8StringEncoding)
            uploadData.appendData(contentDispositionData!)


            // append content type
            //uploadData.appendData("Content-Type: image/png\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!) // mark this. 
            let contentTypeString = "Content-Type: \(postData.mimeType.getString())\r\n\r\n"
            let contentTypeData = contentTypeString.dataUsingEncoding(NSUTF8StringEncoding)
            uploadData.appendData(contentTypeData!)
            uploadData.appendData(postData.data)

        }else{
            uploadData.appendData("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n\(value)".dataUsingEncoding(NSUTF8StringEncoding)!)
        }
    }
    uploadData.appendData("\r\n--\(boundaryConstant)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)



    // return URLRequestConvertible and NSData
    return (Alamofire.ParameterEncoding.URL.encode(mutableURLRequest, parameters: nil).0, uploadData)
}

UTILIZAR:

let docDir:AnyObject = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let imagePath = docDir + "/myPic.jpg"

var imageData = NSData(contentsOfFile: imagePath, options: NSDataReadingOptions.DataReadingMappedIfSafe, error: nil)
var parameters = [
            "pic"           :NetData(nsData: imageData!, filename: "customName.jpg"),
            "otherParm"     :"Value"
        ]


    let urlRequest = self.urlRequestWithComponents("http://www.example.com/upload.php", parameters: parameters)

El NetData de https://github.com/nghialv/Net/blob/master/Net/NetData.swift

upload.php Código:

<?php
// In PHP versions earlier than 4.1.0, $HTTP_POST_FILES should be used instead
// of $_FILES.

$uploaddir = 'uploads/';
// PS: custom filed name : pic
$uploadfile = $uploaddir . basename($_FILES['pic']['name']);

if (move_uploaded_file($_FILES['pic']['tmp_name'], $uploadfile)) {
   $array = array ("code" => "1", "message" => "successfully");  
} else {
   $array = array ("code" => "0", "message" => "Possible file upload attack!".$_FILES['pic']['name']); 
}

echo json_encode ( $array );  

?>

Sería bueno si dijera qué cambio ha realizado. ¿Fue una mejora simple que podría ser una edición de la respuesta de @ antiblank?
Luís Cruz

3
Hola milz, sí, solo algunos cambios de la respuesta de @antiblank, el código me ayuda mucho. Gracias
Vincent Yan

2

Versión más corta basada en las respuestas de @antiblank y @VincentYan.

Clase

class Photo {
    class func upload(image: UIImage, filename: String) -> Request {
        let route = Router.CreatePhoto()
        var request = route.URLRequest.mutableCopy() as NSMutableURLRequest
        let boundary = "NET-POST-boundary-\(arc4random())-\(arc4random())"
        request.setValue("multipart/form-data;boundary="+boundary,
                         forHTTPHeaderField: "Content-Type")

        let parameters = NSMutableData()
        for s in ["\r\n--\(boundary)\r\n",
                  "Content-Disposition: form-data; name=\"photos[photo]\";" +
                    " filename=\"\(filename)\"\r\n",
                  "Content-Type: image/png\r\n\r\n"] {
            parameters.appendData(s.dataUsingEncoding(NSUTF8StringEncoding)!)
        }
        parameters.appendData(UIImageJPEGRepresentation(image, 1))
        parameters.appendData("\r\n--\(boundary)--\r\n"
                               .dataUsingEncoding(NSUTF8StringEncoding)!)
        return Alamofire.upload(request, parameters)
    }
}

Uso

let rep = (asset as ALAsset).defaultRepresentation()
let ref = rep.fullResolutionImage().takeUnretainedValue()
Photo.upload(UIImage(CGImage: ref)!, filename: rep.filename())
    .progress { (bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) in
        println(totalBytesWritten)
    }
    .responseJSON { (request, response, JSON, error) in
        println(JSON)
    }

@TomoMatsumotto Cuando intenté usar su código, recibí un error llamado "Uso del identificador 'Router' sin resolver", creo que Router es una enumeración que está usando aquí. ¿Podría actualizar la respuesta? Thankz
Ankahathara

@Ankahathara Cree una enumeración de enrutador o cree manualmente NSURLRequest en lugar de usar el enrutador. github.com/Alamofire/Alamofire
Tom

2

Si bien hay otras respuestas que aconsejan cómo construir manualmente solicitudes de varias partes, es posible que desee seguir con AFNetworking. Aunque está escrito en Objective-C, aún puede usarlo en sus proyectos Swift (consulte Swift y Objective-C en el mismo proyecto ). De todos modos, el código Swift para enviar una solicitud de varias partes usando AFNetworking es el siguiente:

let data = UIImagePNGRepresentation(image)

let manager = AFHTTPSessionManager()

manager.POST(uploadURLString, parameters: nil, constructingBodyWithBlock: { formData in
    formData.appendPartWithFileData(data, name: "image", fileName: "test.png", mimeType: "image/png")
}, success: { operation, responseObject in
    println(responseObject)
}) { operation, error in
    println(error)
}

Xcode molesto tiene problemas para reconocer este id<AFMultipartFormData>parámetro, formDatapor lo que no disfruta de la típica finalización del código del editor del appendPartWithFileDatamétodo o sus parámetros, pero cuando lo compila y lo ejecuta, funciona bien.


Este es un buen punto, pero creo que la pieza "manual" es algo más pequeña en Alamofire 3. Vea esta respuesta arriba (abajo): stackoverflow.com/a/34961720/8047 ... Gracias
Dan Rosenstark

1

Obtienes 415 porque falta el tipo de contenido en tu solicitud. A continuación se muestra una solución total para cargar imágenes en Swift 2 y AlamoFire

import UIKit
import Alamofire

class ViewController: UIViewController {

@IBOutlet var imageView: UIImageView!
@IBOutlet var btnUpload: UIButton!
override func viewDidLoad() {
    super.viewDidLoad()
}

func successDataHandler(responseData:String){

    print ("IMAGE UPLOAD SUCCESSFUL    !!!")

}

func failureDataHandler(errorData:String){

    print ("  !!!   IMAGE UPLOAD FAILURE   !!! ")

}

@IBAction func actionUpload(sender: AnyObject) {

    let URL = "http://m8coreapibeta.azurewebsites.net/api/cards/SaveImages"

    let postDataProlife:[String:AnyObject] = ["CardId":(dataCardDetail?.userId)!,"ImageType":1,"ImageData":imageView.image!]

    uplaodImageData(URL, postData: postDataProlife, successHandler: successDataHandler, failureHandler: failureDataHandler)
}

func uplaodImageData(RequestURL: String,postData:[String:AnyObject]?,successHandler: (String) -> (),failureHandler: (String) -> ()) -> () {

    let headerData:[String : String] = ["Content-Type":"application/json"]

    Alamofire.request(.POST,RequestURL, parameters: postData, encoding: .URLEncodedInURL, headers: headerData).responseString{ response in
        switch response.result {
        case .Success:
            print(response.response?.statusCode)
            successHandler(response.result.value!)
        case .Failure(let error):
            failureHandler("\(error)")
        }
    }
}
}

0

A continuación se muestran los códigos Swift y Php

Código Swift -> Apple Swift versión 3.0.1 (swiftlang-800.0.58.6 clang-800.0.42.1) Destino: x86_64-apple-macosx10.9

   class  func upload(jsonObject: AnyObject , files : Array<Any>? = nil  , completionHandler :  CompletionBlock? = nil ,failureHandler : FailureBlock? = nil )
{

    Alamofire.upload(multipartFormData:
        { (multipartFormData) in

            if let  filesO = files
            {
                for i in (filesO.enumerated())
                {
                    let image = UIImage(named: "\(i.element)")

                    let data = UIImageJPEGRepresentation(image!, 1)!

                    multipartFormData.append(data, withName: "imgFiles[]" , fileName: "\( NSUUID().uuidString).jpeg" , mimeType: "image/jpeg")
                  // imgFiles[] give array in Php Side
                  // imgFiles   will give string in PHP String


                }

            }


            for  (key, value)  in jsonObject as! [String : String]
            {

                 multipartFormData.append(value.data(using: String.Encoding.utf8)!, withName: key)

            }}          
    },
                     to:baseURL)

Código PHP para obtener parámetros y archivos

Aquí los parámetros se manejan en $ _Request

Y los archivos se manejan en $ _File

El formato en $ _Datos de archivo (matriz, diccionario o cadena) dependerá de la solicitud en el lado rápido, aquí vea esta línea en el código

multipartFormData.append (data, withName: "imgFiles []", fileName: "(NSUUID (). uuidString) .jpeg", mimeType: "image / jpeg")

en el lado de Php withName: "imgFiles []" da una matriz de nombre, formato, tipo

P.ej

"nombre": ["06748B86-478E-421B-8470-6262755AC149.jpeg", "E70269E9-FB54-4BFD-B807-7E418C81540D.jpeg"], "tipo": ["imagen / jpeg", "imagen / jpeg" ], "tmp_name": ["/ tmp / phpz3UAPq", "/ tmp / phpCAPExG"], "error": [0,0], "tamaño": [2779495,2067259]}

Código PHP

 if (isset($_FILES['imgFiles']) and strlen($orderId) > 0) {

        foreach ($_FILES['imgFiles']['tmp_name'] as $key => $tmp_name) {

            $file_name = $key . $_FILES['imgFiles']['name'][$key];
            $file_size = $_FILES['imgFiles']['size'][$key];
            $file_tmp = $_FILES['imgFiles']['tmp_name'][$key];
            $file_type = $_FILES['imgFiles']['type'][$key];
 if (is_dir("$desired_dir/" . $file_name) == false) {
                //move_uploaded_file($file_tmp, "user_data/" . $file_name);
                move_uploaded_file($file_tmp, $desired_dir . "/" .
           $file_name);
            } else {         //rename the file if another one exist
                $new_dir = $desired_dir . "/" . $file_name . time();
                rename($file_tmp, $new_dir);
            }

-2

Tomé la respuesta de antiblank y envolví todo esto en 1 función con controlador de finalización. Pensé que podría ser útil para alguien. Es un poco más 'tosca' que la respuesta de antiblank, ya que simplemente estoy tomando una respuesta de cadena del archivo PHP (no JSON).

Así es como lo llamas:

let imageData = UIImagePNGRepresentation(myImageView.image)

uploadImage("http://www.example.com/image_upload.php", imageData: imageData, subdir: "images", filename: "imageID.png")
    { (req, res, str, err) -> Void in
        // do whatever you want to to for error handling and handeling success
    }

Aquí está la función en sí:

func uploadImage(urlToPHPFile: String, imageData: NSData, subdir: String, filename: String, completionHandler:(request:NSURLRequest, response:NSURLResponse?, responseString:String?, error: NSError?) -> ()) {

    func urlRequestWithComponents(urlString:String, parameters:Dictionary<String, String>, imageData:NSData) -> (URLRequestConvertible, NSData) {
        // create url request to send
        var mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: urlString)!)
        mutableURLRequest.HTTPMethod = Method.POST.rawValue
        let boundaryConstant = "myRandomBoundary12345";
        let contentType = "multipart/form-data;boundary="+boundaryConstant
        mutableURLRequest.setValue(contentType, forHTTPHeaderField: "Content-Type")

        // create upload data to send
        let uploadData = NSMutableData()

        // add image
        uploadData.appendData("\r\n--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
        uploadData.appendData("Content-Disposition: form-data; name=\"file\"; filename=\"file.png\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
        uploadData.appendData("Content-Type: image/png\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
        uploadData.appendData(imageData)

        // add parameters
        for (key, value) in parameters {
            uploadData.appendData("\r\n--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
            uploadData.appendData("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n\(value)".dataUsingEncoding(NSUTF8StringEncoding)!)
        }
        uploadData.appendData("\r\n--\(boundaryConstant)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

        // return URLRequestConvertible and NSData
        return (ParameterEncoding.URL.encode(mutableURLRequest, parameters: nil).0, uploadData)
    }

    let parameters = [
        "subdir" : subdir,
        "filename": filename
    ]
    let urlRequest = urlRequestWithComponents(urlToPHPFile, parameters, imageData)

    AlamoFire.upload(urlRequest.0, urlRequest.1)
        .responseString(completionHandler: { [weak self] (req, res, str, err) -> Void in
            if let strongSelf = self {
                completionHandler(request: req, response: res, responseString: str, error: err)

            }
        }
    )
}

Y aquí está el archivo php.

$subdir = $_POST['subdir'];
$filename = $_POST["filename"];

$targetPath = $subdir.'/'.$filename;

$moved = move_uploaded_file($_FILES["file"]["tmp_name"], $targetPath );
if ($moved) {
    echo "OK";
}
else {
    echo "Error: file not uploaded";
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.