Swift - 从相册中选择照片并上传(使用UIImagePickerController)
(本文代码已升级至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));
?>

只要是用UIImagePickerController从相册选图片,都是那种正方形的框选,没法选原片,有解决办法吗
谢谢航哥,问题之前已经解决了,感谢您的平台,我是自学的开发,如果没有您平台的各种案例讲解,我可能早就坚持不下去了,现在我自己开发的app马上就要上线了,谢谢!
航哥您好,我好像遇到了一个您代码中的一个问题,就是方法一中,如果用户选择了不允许访问,UIImagePickerController.isSourceTypeAvailable(.photoLibrary) 返回的结果还是true,如果选择不允许访问的话并不会执行else中的代码。您可以抽空看一下吗,写程序的过程中刚好碰到了这个问题不知道怎么解决。谢谢谢谢!
是 let filePath = "\(rootPath)/pickedimage.jpg"
还是
let filePath = "\(rootPath)/\(pickedimage)"
是不是我理解错了?
这个厉害了,我几次想解决这个问题几次都失败了,中间耽搁了好久,照这样看来我所搭建一个小app需要的技术已经全部齐全了!真的非常感谢!如果有对应的demo就更好了
航哥,有时间写一篇关于怎么上传和展示多张图片的文章吗 类似于微信朋友圈分享的那种
请问怎么在传图片的同时 传其他参数呢?
我在实际应用中发现了一个问题,当调用UIImagePickerController并弹出相册选择框之后,点导航的返回,只有标题和返回在动,另外viewDidAppear不会被调用。
还有一个问题是self.presentViewController弹出相册选择界面,需要5秒左右的等待。
航哥,我在一个空项目打开系统相册是好使的,但是弄到我项目里,就没有相册了,怎么回事啊
怎么获取相册里的视频呢??
为什么上传图片 控制台报 (lldb) 然后就中断了
涨知识了,谢谢航哥!!!