【问题标题】:Uploading file with parameters using Alamofire使用 Alamofire 上传带参数的文件
【发布时间】:2014-11-25 03:22:06
【问题描述】:

我正在尝试使用Alamofire 上传文件。使用文件 (NSUrl) 时上传工作正常,但是,我似乎无法弄清楚如何使用 NSData 选项?

这是我的测试:

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

我收到状态码 415?

另外,如何在上传中发送其他参数?

谢谢

编辑

我没有设置正确的 Content-Type:

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

仍然无法弄清楚如何在上传时发送其他参数。

【问题讨论】:

  • 使用 "multipartFormData.appendBodyPart(data: image1Data, name: "file", fileName: "myImage.png", mimeType: "image/png")" 否则会得到错误 "Invalid value围绕字符 0"

标签: ios swift alamofire


【解决方案1】:

通过 Swift 3 & 4 和 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)")
          }
  })
}

通过 Swift 2 和 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)
        }
    })
  }

当前的 swift 版本: https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#uploading-data-to-a-server

【讨论】:

  • 如果让 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) }}
  • 如果parameters[String:Any],这将不起作用,因为value.data(using: .utf8))!Any Type 无效。你有建议如何解决这个问题?
【解决方案2】:

下面是swift和php代码

Swift 代码 -> Apple Swift 版本 3.0.1 (swiftlang-800.0.58.6 clang-800.0.42.1) 目标: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)

获取参数和文件的PHP代码

这里的参数是$_Request中的句柄

文件在 $_File 中处理

$_File 中的数据格式(数组、字典或字符串)将取决于 swift 端的请求,此处参见代码中的这一行

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

在 PHP 端 withName: "imgFiles[]" 给出名称、格式、类型的数组

例如

“名称”:[“06748B86-478E-421B-8470-6262755AC149.jpeg”,“E70269E9-FB54-4BFD-B807-7E418C81540D.jpeg”],“类型”:[“图像/jpeg”,“图像/JPEG"], "tmp_name":["/tmp/phpz3UAPq","/tmp/phpCAPExG"], "错误":[0,0], “大小”:[2779495,2067259]}

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

【讨论】:

    【解决方案3】:

    增强 EdFunke 对 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】:

      您收到 415 是因为您的请求中缺少 内容类型。 以下是在 Swift 2 和 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)")
              }
          }
      }
      }
      

      【讨论】:

        【解决方案5】:

        这是一个使用基于 antiblanks 答案的 Alamofire 3.0 的解决方案:

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

        【讨论】:

        • 如何取消此上传?在其他答案中,我看到人们说要上传到 var,但是当我这样做时,类型被推断为 (),因此不允许调用方法?谢谢。
        • @SeanLintern88:您可以在请求对象上调用 cancel()。类似于:upload.cancel()。您可以将 request-Object 分配给一个变量并调用 cancel()
        • case .Success(let upload, _, _) upload 找不到。我错过了什么吗?
        • @fatihyildizhan encodingResult 是一个 MultipartFormDataEncodingResult 枚举。 .Success Case 有以下参数: (request: Request, streamingFromDisk: Bool, streamFileURL: NSURL?) 所以upload 是一个请求。你加import Alamofire了吗?
        • 你是如何在这方面保持进步的?
        【解决方案6】:

        分段上传计划包含在 Alamofire 的下一个 (1.3.0) 版本中。 同时,使用此线程中的信息,我创建了一个类,它简化了上传文件并在请求中包含附加参数(常规“输入”)以及一个或多个文件。 不假设文件属于特定类型或使用路由器。

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

        它会这样使用:

        // 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)
        

        查看或从此 gist 下载:https://gist.github.com/ncerezo/b1991f8dfac01cb162c0

        【讨论】:

        • 嗨,在你的例子中,有没有办法在 iOS 端检查上传过程是否成功?感谢您的回答。
        • 是的,当然。正如我所提到的,uploadFile(request) 方法返回一个 Request 对象,就像 Alamofire.request 方法一样,因此您可以简单地链接进度和/或响应闭包。例如:fileUploader.uploadFile(request: request).response { (request,response,data,error) in .... }
        【解决方案7】:

        虽然有其他答案建议如何手动构建多部分请求,但您可能只想坚持使用 AFNetworking。即使它是用 Objective-C 编写的,你仍然可以在你的 Swift 项目中使用它(参见Swift and Objective-C in the Same Project)。无论如何,使用 AFNetworking 提交多部分请求的 Swift 代码如下:

        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 很烦人在识别这个id&lt;AFMultipartFormData&gt; 参数formData 时遇到问题,所以你不喜欢appendPartWithFileData 方法或其参数的典型编辑器代码完成,但是当你编译它并运行它时,它工作正常。

        【讨论】:

        • 这是一个很好的观点,但我认为 Alamofire 3 中的“手动”部分要小一些。请参阅上面的答案(下):stackoverflow.com/a/34961720/8047 ... 谢谢
        【解决方案8】:

        我接受了 antiblank 的回答,并将这一切都包含在 1 个带有完成处理程序的函数中。认为它可能对某人有用。这比 antiblank 的回答有点“粗糙”,因为我只是从 PHP 文件(不是 JSON)中获取字符串响应。

        你是这样称呼它的:

        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
            }
        

        这是函数本身:

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

        这里是 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";
        }
        

        【讨论】:

          【解决方案9】:

          基于@antiblank 和@VincentYan 答案的较短版本。

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

          用法

          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 当我尝试使用您的代码时,我收到名为“使用未解析的标识符'Router'”的错误,我认为路由器是您在此处使用的枚举。你能更新答案吗?谢谢
          • @Ankahathara 创建路由器枚举或手动创建 NSURLRequest 而不是使用路由器。 github.com/Alamofire/Alamofire
          【解决方案10】:

          @antiblank 答案中的代码对我不起作用。我做了一些更改,现在可以使用了:

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

          使用:

          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)
          

          来自https://github.com/nghialv/Net/blob/master/Net/NetData.swift的NetData

          上传.php代码:

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

          【讨论】:

          • 如果您能说明您所做的更改,那就太好了。这是一个简单的改进,可以编辑@antiblank 的答案吗?
          • 嗨 milz ,是的,与@antiblank 答案相比只有很少的变化,代码对我很有帮助。谢谢
          【解决方案11】:

          这是一个简单的函数,它需要目标上传url、参数和imageData,并返回Alamofire.upload上传带参数图像所需的URLRequestConvertible和NSData。

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

          这是一个如何使用它的示例(请参阅创建和发送请求):

          // 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)")
          }    
          

          如果您需要目标 url 的 php 文件(在同一目录中有一个“上传”文件夹):

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

          【讨论】:

          • 嗨,我已经使用了这种方法并且效果很好。但是在服务器端他们更改了请求内容类型 JSON。之后这种方法不起作用。我想将数据上传为 json...你能不能帮帮我
          • 几乎正确,uploadData.appendData("Content-Disposition: form-data; name=\"file\"; filename=\"file.png\"\r\n".dataUsingEncoding(NSUTF8StringEncoding )!) 在这一行中 name=\"file\" ,确实 file 是服务器处理的键名..就像回形针一样。
          • Mines 以前工作,现在我得到 NSCocoaErrorDomain 代码 3840,有人解决了这个问题吗?
          • @antiblank 感谢您分享此代码。您能否建议哦如何更改此函数,以便它可以接受图片参数的 NSData 或 null,因为用户可以选择是否在我的 Web 服务调用中上传图片。我可以在定义中使用 anyObject 而不是 NSData
          • 我们怎样才能做到这一点,包括标题?
          猜你喜欢
          • 2016-10-06
          • 2023-03-22
          • 2017-10-13
          • 2019-08-11
          • 2019-08-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多