Swift - 从相册中选择照片并上传(使用UIImagePickerController)
作者:hangge | 2016-05-13 08:35
(本文代码已升级至Swift3)
我们使用 UIImagePickerController(图片选择器)可以很方便的从系统“照片”中选择图片,但我们会发现选择完毕后,通过图片的 info[UIImagePickerControllerReferenceURL] 得到的是一个引用路径,格式如下:
assets-library://asset/asset.PNG?id=90B54369-5E79-433D-B74A-E8E0870EAF27&ext=PNG用这个路径是没法上传文件的。想要把选择的图片上传,通常我们会想到如下两种方式:
方法一:先将图片保存到一个临时文件夹下,再上传
下面样例在 imagePickerController 选择图片后,使用 fileManager 将其复制保存到应用的文档目录下,再将复制过来的图片上传。
import UIKit import Alamofire class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { override func viewDidLoad() { super.viewDidLoad() } //选取相册 @IBAction func fromAlbum(_ sender: Any) { //判断设置是否支持图片库 if UIImagePickerController.isSourceTypeAvailable(.photoLibrary){ //初始化图片控制器 let picker = UIImagePickerController() //设置代理 picker.delegate = self //指定图片控制器类型 picker.sourceType = .photoLibrary //弹出控制器,显示界面 self.present(picker, animated: true, completion: { () -> Void in }) }else{ print("读取相册错误") } } //选择图片成功后代理 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { //获取选择的原图 let pickedImage = info[UIImagePickerControllerOriginalImage] as! UIImage //将选择的图片保存到Document目录下 let fileManager = FileManager.default let rootPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String let filePath = "\(rootPath)/pickedimage.jpg" let imageData = UIImageJPEGRepresentation(pickedImage, 1.0) fileManager.createFile(atPath: filePath, contents: imageData, attributes: nil) //上传图片 if (fileManager.fileExists(atPath: filePath)){ //取得NSURL let imageURL = URL(fileURLWithPath: filePath) //使用Alamofire上传 Alamofire.upload(imageURL, to: "http://www.hangge.com/upload.php") .responseString { response in print("Success: \(response.result.isSuccess)") print("Response String: \(response.result.value ?? "")") } } //图片控制器退出 picker.dismiss(animated: true, completion:nil) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }不管使用模拟器还是真机调试,运行后可以看到图片上传成功了:
方法二:使用PhotoKit获取选择图片的真实路径,再上传
import UIKit import Alamofire import Photos class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { override func viewDidLoad() { super.viewDidLoad() } //选取相册 @IBAction func fromAlbum(_ sender: Any) { //判断设置是否支持图片库 if UIImagePickerController.isSourceTypeAvailable(.photoLibrary){ //初始化图片控制器 let picker = UIImagePickerController() //设置代理 picker.delegate = self //指定图片控制器类型 picker.sourceType = .photoLibrary //弹出控制器,显示界面 self.present(picker, animated: true, completion: { () -> Void in }) }else{ print("读取相册错误") } } //选择图片成功后代理 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { //选择图片的引用路径 let pickedURL = info[UIImagePickerControllerReferenceURL] as! URL let fetchResult: PHFetchResult = PHAsset.fetchAssets(withALAssetURLs: [pickedURL], options: nil) let asset = fetchResult.firstObject PHImageManager.default() .requestImageData(for: asset!, options: nil, resultHandler: { (imageData:Data?, dataUTI:String?, orientation:UIImageOrientation, info:[AnyHashable : Any]?) in //获取实际路径 let imageURL = info!["PHImageFileURLKey"] as! URL print("路径:",imageURL) print("文件名:",imageURL.lastPathComponent) //使用Alamofire上传 Alamofire.upload(imageURL, to: "http://www.hangge.com/upload.php") .responseString { response in print("Success: \(response.result.isSuccess)") print("Response String: \(response.result.value ?? "")") } }) //图片控制器退出 picker.dismiss(animated: true, completion:nil) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }使用模拟器运行后,可以看到图片上传成功了:
但如果使用真机调试的话,虽然我们得到了图片的真实路径和文件名,但还是无法上传。所以上传图片还是建议使用方法一。
附录:
(1)本文样例使用 Alamofire 上传文件,对于Alamofire不熟悉的可参考我原来写过的几篇文章:
(2)服务端php代码如下:
<?php /** php 接收流文件 * @param String $file 接收后保存的文件名 * @return boolean */ function receiveStreamFile($receiveFile){ $streamData = isset($GLOBALS['HTTP_RAW_POST_DATA'])? $GLOBALS['HTTP_RAW_POST_DATA'] : ''; if(empty($streamData)){ $streamData = file_get_contents('php://input'); } if($streamData!=''){ $ret = file_put_contents($receiveFile, $streamData, true); }else{ $ret = false; } return $ret; } //定义服务器存储路径和文件名 $receiveFile = $_SERVER["DOCUMENT_ROOT"]."/uploadFiles/hangge.zip"; $ret = receiveStreamFile($receiveFile); echo json_encode(array('success'=>(bool)$ret)); ?>
全部评论(12)
只要是用UIImagePickerController从相册选图片,都是那种正方形的框选,没法选原片,有解决办法吗
站长回复:默认情况下UIImagePickerController就是选原片的啊。除非你把它的allowsEditing属性设置为true,才会出现正方形的框选,你检查下这个属性。
谢谢航哥,问题之前已经解决了,感谢您的平台,我是自学的开发,如果没有您平台的各种案例讲解,我可能早就坚持不下去了,现在我自己开发的app马上就要上线了,谢谢!
站长回复:不客气,能帮助到你我也很高兴。这里也祝你开发的APP早日成功上线,加油!
航哥您好,我好像遇到了一个您代码中的一个问题,就是方法一中,如果用户选择了不允许访问,UIImagePickerController.isSourceTypeAvailable(.photoLibrary) 返回的结果还是true,如果选择不允许访问的话并不会执行else中的代码。您可以抽空看一下吗,写程序的过程中刚好碰到了这个问题不知道怎么解决。谢谢谢谢!
站长回复:UIImagePickerController.isSourceTypeAvailable(.photoLibrary) 只是用来判断设备是否有图片库,跟是否有访问权限没关系。
如果需要判断是否允许访问,可以再外面使用 PHPhotoLibrary.authorizationStatus() 进行判断,具体可以参考我之前写的这篇文章:Swift - 判断是否有某功能访问权限,没有则提示,并自动跳转到设置页
是 let filePath = "\(rootPath)/pickedimage.jpg"
还是
let filePath = "\(rootPath)/\(pickedimage)"
是不是我理解错了?
站长回复:是 let filePath = "\(rootPath)/pickedimage.jpg"
这个厉害了,我几次想解决这个问题几次都失败了,中间耽搁了好久,照这样看来我所搭建一个小app需要的技术已经全部齐全了!真的非常感谢!如果有对应的demo就更好了
站长回复:不客气。欢迎常来看看,我会持续更新下去的。
航哥,有时间写一篇关于怎么上传和展示多张图片的文章吗 类似于微信朋友圈分享的那种
站长回复:图片的多选功能可以参考我写的这篇文章:Swift - 相册图片多选功能的实现
选择完毕后将图片上传即可。
请问怎么在传图片的同时 传其他参数呢?
站长回复:参考我写的Alamofire文件上传相关文章,里面有讲如何带上其它参数:Swift - HTTP网络操作库Alamofire使用详解2(文件上传)
我在实际应用中发现了一个问题,当调用UIImagePickerController并弹出相册选择框之后,点导航的返回,只有标题和返回在动,另外viewDidAppear不会被调用。
还有一个问题是self.presentViewController弹出相册选择界面,需要5秒左右的等待。
站长回复:我又测试了下,viewDidAppear是会调用的,而且界面弹出不需要等5秒那么久。你那边出现的问题很奇怪,我目前也想不出是什么原因。
航哥,我在一个空项目打开系统相册是好使的,但是弄到我项目里,就没有相册了,怎么回事啊
站长回复:可能是跟你项目中某个代码冲突,具体需要你自己调试了。
怎么获取相册里的视频呢??
站长回复:下个月会写篇相关文章,你可以关注下。
为什么上传图片 控制台报 (lldb) 然后就中断了
站长回复:不太清楚,我测试了下是好的。
涨知识了,谢谢航哥!!!
站长回复:不客气!