使用节点后端在Swift中将图像上传到AWS S3

众所周知,在任何应用程序中处理图像上传都是棘手的

如果此内容对您来说有点繁重,请看一下我有关移动后端开发的课程。

许多应用程序需要某种图像上传功能,但是实现此功能意味着解决一些不同的问题。 开发人员必须决定要将图像存储在何处以及如何安全地到达那里。 必须使用远程存储解决方案; 您不想浪费服务器的带宽或介质上的磁盘速度。

在异步Web应用程序或移动应用程序中实现此功能会带来另一个问题:如果通过服务器进行身份验证,我们是否真的必须将图像发布到服务器上,然后才将它们移动到永久位置?

亚马逊S3

Amazon Simple Storage Service(S3)可以为我们解决所有这些问题。 S3 存储桶是用于存储文件的存储单元。 在本指南中,我将向您展示如何使用Node.js后端向经过身份验证的用户发布签名的URL ,每个URL都将允许我们的Swift应用程序直接将单个文件上传到S3存储桶。 上传的图片将在其自己的URL上可用。

总览

首先,我将向您展示如何设置存储桶。 然后,我将编写一个最小的Express应用程序,该应用程序生成签名的URL-您可以自己实现身份验证(请参阅我的移动后端开发课程)。 最后,我将向您展示如何使用Alamofire检索签名的URL,然后使用该URL上传图像。

设置桶

首先,注册Amazon Web服务,然后导航到S3仪表板。 选择创建存储桶并输入名称; 我通常使用域名。 将区域设置为俄勒冈州-这对应于“ us-west-2”区域,我将在稍后的配置中使用该区域。 点击创建

现在,在存储桶的仪表板上,选择右上角的“ 属性 ”,然后在“ 权限”下,单击“ 添加存储桶策略” 。 存储桶策略由一些看起来很神秘的JSON组成,但是我们在此处添加的策略仅允许任何人查看存储桶中的任何文件。

复制以下内容并将其粘贴为存储桶策略,然后点击保存

  { 
“ Version”:“ 2008-10-17”,
“声明”:[
{
“ Sid”:“ AllowPublicRead”,
“效果”:“允许”,
“校长”:{
“ AWS”:“ *”
},
“ Action”:“ s3:GetObject”,
“资源”:“ arn:aws:s3 ::: leasd / *”
}
]
}

现在,我们只需要创建一个秘密密钥,这将允许我们的后端生成签名的URL。 通过从右上角的帐户下拉菜单中选择“ 安全凭据” ,导航到IAM仪表板。

从侧面菜单中选择“ 用户 ”,然后单击“ 创建新用户” 。 提出新用户的用户名,并在第一个输入字段中输入; 我通常使用 -s3作为用户名。 然后点击创建 。 现在,单击“ 显示用户安全凭据”,然后将其复制并粘贴到安全的位置-不要丢失这些凭据,否则您将不得不创建一个新用户。 您还必须永远不要泄漏它们:将它们与源代码隔离。

现在,单击关闭 (两次),然后从列表中选择新用户。 选择权限,然后点击附加策略

勾选AmazonS3FullAccess ,然后单击附加策略

这使该用户能够对我们的任何S3存储桶执行任何操作。 我们的应用程序使用我们之前复制的凭据来执行此操作。

是时候编写我们的Node后端了,该后端将发布签名的URL。

节点后端

在本节中,我将假设您知道如何使用Node和Express,并向您抛出一堆代码。 我在这里使用的唯一特有模块是aws-sdk ,它使我们可以使用一种通过密钥生成签名的S3 URL的方法,该方法将存储在名为config.js的文件中。

在实际的应用程序中,您将把所有这些代码分离到路由器和控制器中,并且在实际发布签名的URL之前,您需要进行一些身份验证。

 // index.js 

var express = require('express');
var bodyParser = require('body-parser');
var morgan = require('morgan'); //用于记录
var uuid = require('uuid');
var aws = require('aws-sdk');

var config = require('./ config');

var s3 = new aws.S3();
s3.config.update({accessKeyId:config.accessKeyId,secretAccessKey:config.secretAccessKey});

函数getSignedURL(req,res,next){
var params = {
值区:“ xxx”,//您的值区名称
密钥:uuid.v4(),//这会生成一个唯一的标识符
过期:100,//必须发布图片的秒数
ContentType:'image / jpeg'//必须与Alamofire PUT请求的“ Content-Type”标头匹配
};
s3.getSignedUrl('putObject',params,function(err,signedURL){
如果(错误){
console.log(err);
返回next(err);
}其他{
返回res.json({postURL:signedURL,getURL:signedURL.split(“?”)[0]});
}
});
}

//标准快递资料

var app = express();
var router = express.Router();

router.route('/ get_signed_url')
.get(getSignedURL);

app.use(morgan('combined'));
app.use(bodyParser.json());
app.use('/ v1',路由器);

var port = process.env.PORT || 3000;
var host = process.env.HOST || '127.0.0.1';

console.log(“监听”,主机,端口);
app.listen(端口,主机);

config.js看起来像这样:

  // config.js 
  module.exports = { 
accessKeyId:'AXXXXXXXXXXXXXXXXXXXXA',
secretAccessKey:“ PXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo”,
地区:“ us-west-2” //重要
}

并确保将您的秘密密钥(以及您的node_modules /目录)与.gitignore文件保持在版本控制之外:

  #.gitignore 
  node_modules / 
config.js

现在,在初始化节点包并安装必要的模块之后,如果使用节点index.js运行应用程序,并导航至http:// localhost:3000 / v1 / get_signed_url,您应该会看到一些带有postURL和getURL属性的JSON。 太棒了

时间快一点。

在Swift中上传图片

签出Alamofire存储库,并按照其安装说明进行操作。 我们还将使用名为SwiftyJSON的模块,因此也要安装该pod。 这是我的Podfile的样子:

  #Podfile 
 来源'https://github.com/CocoaPods/Specs.git' 
平台:ios,“ 9.0”
use_frameworks!
 目标'XcodeProjectName'做 
pod'Alamofire','〜> 3.4'
pod'SwiftyJSON'
结束

安装完这些文件后,我们还需要在Xcode项目中启用不安全的HTTP通信。 右键单击info.plist ,然后单击“ 另存为…”>“源代码” 。 将以下突出显示的行添加到文件的底部:

现在,我们的Xcode项目已准备好一些代码。 在您的Xcode项目中创建一个名为ImagePost.swift和c opy的新Swift文件,并粘贴以下代码:

  // 
// ImagePost.swift
//使用节点后端在Swift中将图像上传到AWS S3
//
 进口Alamofire 
导入SwiftyJSON
 类ImagePost { 

静态让getTokenURL =“ http:// localhost:3000 / v1 / get_signed_url”

私有静态函数performUpload(image:UIImage,postURL:String,getURL:String,completionHandler:(成功:Bool,getURL:String?)->()){
如果让imageData = UIImageJPEGRepresentation(image,0.1){// 0.1用于高压缩
打印(“正在上传!挂在那里...”)
let request = Alamofire.upload(.PUT,postURL,标头:[“ Content-Type”:“ image / jpeg”],data:imageData)
request.validate()
request.response {(req,res,json,err)在
如果err!= nil {
打印(“ ERR \(err)”)
//将compltionHandler调度到主线程(后台进程
//永远不要操纵UI,completionHandler将
//可能包含segue或其他内容)
dispatch_async(dispatch_get_main_queue(),{
completeHandler(成功:false,getURL:getURL)
})
}其他{
dispatch_async(dispatch_get_main_queue(),{
completeHandler(成功:true,getURL:getURL)
})
}
}
}

}

静态函数uploadImage(image:UIImage,completeHandler:(success:Bool,getURL:String?)->()){
let request = Alamofire.request(.GET,ImagePost.getTokenURL,编码:.JSON)
request.validate()
request.responseJSON {
切换response.result {
案例。成功:
如果让值= response.result.value {
让json = JSON(值)
如果让postURL = json [“ postURL”]。string,getURL = json [“ getURL”]。string {
打印(postURL)
performUpload(图片,postURL:postURL,getURL:getURL,completeHandler:complementHandler)
返回
}
}
completeHandler(成功:false,getURL:nil)
case .Failure(错误):
打印(“ ERR \(响应)\(错误)”)
completeHandler(成功:false,getURL:nil)
}
}
}
}

现在,在此Xcode项目的任何地方,您都应该能够调用ImagePost.uploadImage,传入一个UIImage,以及一个完成处理程序,该处理程序带有两个参数:一个布尔值,指示上载是否成功,以及一个可选的String,用于在该位置上上传图像现在可以在找到。

尽管您应该快速浏览一下SwiftyJSON和Alamofire的文档,但上面的代码对于具有快速经验的任何人来说都应该很容易理解。

现在,我将粘贴我能想到的最简单的用法示例。 这只是一个ViewController,它会弹出一个图像选择器,当您选择图像时,它将上载到S3,并记录图像的URL。 如果创建的新Xcode项目是简单的单视图应用程序,则可以简单地将以下代码复制并粘贴到ViewController.swift中并运行模拟器。

以下代码从上到下执行,仅在选择图像后才调用imagePickerController didFinishPickingWithInfo。 选择图像后,图像选择器将立即重新出现,但只需查看日志以获取反馈即可。

  // 
// ViewController.swift
//通过节点后端在Swift中将图像上传到AWS S3
//

导入UIKit
进口Alamofire
导入SwiftyJSON

ViewController类:UIViewController,UIImagePickerControllerDelegate,UINavigationControllerDelegate {

让imagePicker = UIImagePickerController()
var image:UIImage?

覆盖func viewDidLoad(){
super.viewDidLoad()

imagePicker.delegate =自我
imagePicker.allowsEditing = false
imagePicker.sourceType = .PhotoLibrary
}

覆盖func viewDidAppear(动画:布尔){
super.viewDidAppear(true)

presentViewController(imagePicker,动画:true,完成:无)
}

func imagePickerController(picker:UIImagePickerController,didFinishPickingMediaWithInfo信息:[String:AnyObject]){
如果让pickImage = = info [UIImagePickerControllerOriginalImage]为? UIImage {
self.image = PickedImage
}
dismissViewControllerAnimated(true,完成:无)


如果让image = self.image {
ImagePost.uploadImage(image,completionHandler:{(成功,getURL)在
如果(成功){
打印(getURL)
}其他{
print(“无法上传:(”)
}
})
}
}
}

就这样。 如果您对任何上述代码有任何疑问,请在下面留下评论; 它应该很简单。