AWS SDK 教程
1. 什么是 AWS SDK?
AWS SDK (Software Development Kit) 是 Amazon Web Services 提供的开发工具包,它允许开发者在自己的应用中轻松集成 AWS 服务。AWS SDK for JavaScript 是专门为 JavaScript 和 Node.js 开发者设计的 SDK,支持浏览器和 Node.js 环境。
2. AWS SDK 的核心概念
2.1 客户端 (Client)
客户端是与 AWS 服务交互的主要接口,每个 AWS 服务都有对应的客户端类。
2.2 命令 (Command)
命令是客户端执行的操作,每个 AWS 服务操作都对应一个命令类。
2.3 配置 (Configuration)
配置包含访问密钥、区域、端点等信息,用于初始化客户端。
2.4 凭证 (Credentials)
凭证用于验证对 AWS 服务的访问权限,包括访问密钥 ID 和秘密访问密钥。
2.5 区域 (Region)
区域是 AWS 服务的物理位置,选择合适的区域可以减少延迟并满足合规要求。
2.6 Promise 和异步操作
AWS SDK for JavaScript v3 使用 Promise 和 async/await 语法处理异步操作。
3. AWS SDK 的安装和配置
3.1 安装 AWS SDK
使用 npm 或 yarn 安装 AWS SDK for JavaScript:
# 安装整个 SDK
npm install aws-sdk
# 或者安装特定服务的客户端(推荐,减小包体积)
npm install @aws-sdk/client-s3 @aws-sdk/client-dynamodb @aws-sdk/client-lambda3.2 配置凭证
AWS SDK 支持多种方式配置凭证:
- 环境变量:设置
AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY环境变量 - AWS 配置文件:在
~/.aws/credentials文件中配置凭证 - IAM 角色:在 EC2、Lambda 等服务中使用 IAM 角色
- 临时凭证:使用 STS 服务获取临时凭证
3.3 初始化客户端
// 方式 1:使用默认配置
const { S3Client } = require('@aws-sdk/client-s3');
const s3Client = new S3Client();
// 方式 2:指定配置
const { S3Client } = require('@aws-sdk/client-s3');
const s3Client = new S3Client({
region: 'us-east-1',
credentials: {
accessKeyId: 'your-access-key-id',
secretAccessKey: 'your-secret-access-key'
}
});4. AWS SDK 的基本使用
4.1 使用 S3 服务
上传文件到 S3:
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const fs = require('fs');
const s3Client = new S3Client({ region: 'us-east-1' });
async function uploadFile(bucketName, key, filePath) {
const fileContent = fs.readFileSync(filePath);
const command = new PutObjectCommand({
Bucket: bucketName,
Key: key,
Body: fileContent,
ContentType: 'text/plain'
});
try {
const response = await s3Client.send(command);
console.log('File uploaded successfully:', response);
return response;
} catch (error) {
console.error('Error uploading file:', error);
throw error;
}
}
// 使用示例
uploadFile('my-bucket', 'example.txt', './example.txt');下载 S3 中的文件:
const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3');
const fs = require('fs');
const s3Client = new S3Client({ region: 'us-east-1' });
async function downloadFile(bucketName, key, filePath) {
const command = new GetObjectCommand({
Bucket: bucketName,
Key: key
});
try {
const response = await s3Client.send(command);
const fileContent = await response.Body.transformToString();
fs.writeFileSync(filePath, fileContent);
console.log('File downloaded successfully');
return fileContent;
} catch (error) {
console.error('Error downloading file:', error);
throw error;
}
}
// 使用示例
downloadFile('my-bucket', 'example.txt', './downloaded-example.txt');4.2 使用 DynamoDB 服务
创建表:
const { DynamoDBClient, CreateTableCommand } = require('@aws-sdk/client-dynamodb');
const dynamoDBClient = new DynamoDBClient({ region: 'us-east-1' });
async function createTable(tableName) {
const command = new CreateTableCommand({
TableName: tableName,
KeySchema: [
{ AttributeName: 'id', KeyType: 'HASH' }
],
AttributeDefinitions: [
{ AttributeName: 'id', AttributeType: 'S' }
],
ProvisionedThroughput: {
ReadCapacityUnits: 5,
WriteCapacityUnits: 5
}
});
try {
const response = await dynamoDBClient.send(command);
console.log('Table created successfully:', response);
return response;
} catch (error) {
console.error('Error creating table:', error);
throw error;
}
}
// 使用示例
createTable('users');写入数据:
const { DynamoDBClient, PutItemCommand } = require('@aws-sdk/client-dynamodb');
const { marshall } = require('@aws-sdk/util-dynamodb');
const dynamoDBClient = new DynamoDBClient({ region: 'us-east-1' });
async function putItem(tableName, item) {
const command = new PutItemCommand({
TableName: tableName,
Item: marshall(item)
});
try {
const response = await dynamoDBClient.send(command);
console.log('Item written successfully:', response);
return response;
} catch (error) {
console.error('Error writing item:', error);
throw error;
}
}
// 使用示例
putItem('users', {
id: '1',
name: 'John Doe',
email: 'john@example.com'
});读取数据:
const { DynamoDBClient, GetItemCommand } = require('@aws-sdk/client-dynamodb');
const { marshall, unmarshall } = require('@aws-sdk/util-dynamodb');
const dynamoDBClient = new DynamoDBClient({ region: 'us-east-1' });
async function getItem(tableName, key) {
const command = new GetItemCommand({
TableName: tableName,
Key: marshall(key)
});
try {
const response = await dynamoDBClient.send(command);
const item = response.Item ? unmarshall(response.Item) : null;
console.log('Item retrieved successfully:', item);
return item;
} catch (error) {
console.error('Error retrieving item:', error);
throw error;
}
}
// 使用示例
getItem('users', { id: '1' });4.3 使用 Lambda 服务
调用 Lambda 函数:
const { LambdaClient, InvokeCommand } = require('@aws-sdk/client-lambda');
const lambdaClient = new LambdaClient({ region: 'us-east-1' });
async function invokeLambda(functionName, payload) {
const command = new InvokeCommand({
FunctionName: functionName,
Payload: JSON.stringify(payload)
});
try {
const response = await lambdaClient.send(command);
const result = JSON.parse(Buffer.from(response.Payload).toString());
console.log('Lambda function invoked successfully:', result);
return result;
} catch (error) {
console.error('Error invoking Lambda function:', error);
throw error;
}
}
// 使用示例
invokeLambda('my-function', { name: 'John Doe' });4. AWS SDK 的高级功能
4.1 使用中间件
中间件可以在命令执行前后添加自定义逻辑,例如日志记录、重试、超时处理等:
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const { Logger } = require('@aws-sdk/lib-dynamodb');
const s3Client = new S3Client({
region: 'us-east-1',
middlewareStack: (stack) => {
stack.add(
(next) => async (args) => {
console.log('Before sending command:', args.commandName);
const result = await next(args);
console.log('After sending command:', result);
return result;
},
{ step: 'finalizeRequest' }
);
}
});4.2 使用分页器
分页器用于处理返回大量数据的 API 操作,例如列出 S3 存储桶中的所有对象:
const { S3Client, ListObjectsV2Command } = require('@aws-sdk/client-s3');
const { paginateListObjectsV2 } = require('@aws-sdk/util-utf8-node');
const s3Client = new S3Client({ region: 'us-east-1' });
async function listAllObjects(bucketName) {
const paginator = paginateListObjectsV2(
{ client: s3Client },
{ Bucket: bucketName }
);
const allObjects = [];
for await (const page of paginator) {
if (page.Contents) {
allObjects.push(...page.Contents);
}
}
console.log('All objects retrieved successfully:', allObjects.length);
return allObjects;
}
// 使用示例
listAllObjects('my-bucket');4.3 使用 Waiters
Waiters 用于等待 AWS 资源达到特定状态,例如等待 EC2 实例启动完成:
const { EC2Client, RunInstancesCommand, WaiterState } = require('@aws-sdk/client-ec2');
const { waitUntilInstanceRunning } = require('@aws-sdk/client-ec2');
const ec2Client = new EC2Client({ region: 'us-east-1' });
async function runInstanceAndWait() {
// 启动实例
const runCommand = new RunInstancesCommand({
ImageId: 'ami-0c55b159cbfafe1f0', // Amazon Linux 2 AMI
InstanceType: 't2.micro',
MinCount: 1,
MaxCount: 1
});
const runResponse = await ec2Client.send(runCommand);
const instanceId = runResponse.Instances[0].InstanceId;
console.log('Instance started:', instanceId);
// 等待实例运行
await waitUntilInstanceRunning(
{
client: ec2Client,
maxWaitTime: 60 // 最大等待时间(秒)
},
{
InstanceIds: [instanceId]
}
);
console.log('Instance is now running:', instanceId);
return instanceId;
}
// 使用示例
runInstanceAndWait();4.4 使用预设凭证提供者
预设凭证提供者可以自动从环境变量、配置文件、IAM 角色等来源获取凭证:
const { S3Client } = require('@aws-sdk/client-s3');
const { fromEnv, fromIni, fromInstanceMetadata } = require('@aws-sdk/credential-providers');
// 从环境变量获取凭证
const s3Client1 = new S3Client({
region: 'us-east-1',
credentials: fromEnv()
});
// 从配置文件获取凭证
const s3Client2 = new S3Client({
region: 'us-east-1',
credentials: fromIni({ profile: 'my-profile' })
});
// 从 EC2 实例元数据获取凭证
const s3Client3 = new S3Client({
region: 'us-east-1',
credentials: fromInstanceMetadata()
});5. AWS SDK 的最佳实践
5.1 安全最佳实践
- 不要硬编码凭证:使用环境变量、配置文件或 IAM 角色
- 使用最小权限原则:为应用授予必要的最小权限
- 定期轮换凭证:定期更新访问密钥
- 使用 HTTPS:始终使用 HTTPS 与 AWS 服务通信
5.2 性能最佳实践
- 使用特定服务的客户端:只安装和使用需要的服务客户端,减小包体积
- 重用客户端实例:避免频繁创建客户端实例
- 使用连接池:对于需要频繁连接的服务,使用连接池
- 选择合适的区域:选择离用户最近的区域,减少延迟
5.3 错误处理最佳实践
- 捕获和处理特定错误:根据错误代码采取不同的处理策略
- 实现重试机制:对于临时错误,实现指数退避重试
- 记录错误信息:详细记录错误信息,便于调试
- 设置合理的超时:避免请求无限期等待
5.4 代码组织最佳实践
- 模块化:将 AWS 服务相关代码组织成模块
- 使用异步/await:使用现代 JavaScript 语法处理异步操作
- 编写单元测试:为 AWS SDK 代码编写单元测试
- 使用 TypeScript:使用 TypeScript 提高代码可靠性
6. 实际应用示例
6.1 构建一个文件上传服务
const express = require('express');
const multer = require('multer');
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const { v4: uuidv4 } = require('uuid');
const app = express();
const upload = multer({ storage: multer.memoryStorage() });
const s3Client = new S3Client({ region: 'us-east-1' });
const bucketName = 'my-file-upload-bucket';
app.post('/upload', upload.single('file'), async (req, res) => {
try {
const file = req.file;
const key = `${uuidv4()}-${file.originalname}`;
const command = new PutObjectCommand({
Bucket: bucketName,
Key: key,
Body: file.buffer,
ContentType: file.mimetype
});
await s3Client.send(command);
const fileUrl = `https://${bucketName}.s3.${process.env.AWS_REGION}.amazonaws.com/${key}`;
res.json({ success: true, fileUrl });
} catch (error) {
console.error('Error uploading file:', error);
res.status(500).json({ success: false, error: 'Failed to upload file' });
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});6.2 构建一个用户数据存储服务
const express = require('express');
const { DynamoDBClient, PutItemCommand, GetItemCommand, ScanCommand } = require('@aws-sdk/client-dynamodb');
const { marshall, unmarshall } = require('@aws-sdk/util-dynamodb');
const { v4: uuidv4 } = require('uuid');
const app = express();
app.use(express.json());
const dynamoDBClient = new DynamoDBClient({ region: 'us-east-1' });
const tableName = 'users';
// 创建用户
app.post('/users', async (req, res) => {
try {
const user = {
id: uuidv4(),
...req.body
};
const command = new PutItemCommand({
TableName: tableName,
Item: marshall(user)
});
await dynamoDBClient.send(command);
res.json({ success: true, user });
} catch (error) {
console.error('Error creating user:', error);
res.status(500).json({ success: false, error: 'Failed to create user' });
}
});
// 获取用户
app.get('/users/:id', async (req, res) => {
try {
const { id } = req.params;
const command = new GetItemCommand({
TableName: tableName,
Key: marshall({ id })
});
const response = await dynamoDBClient.send(command);
const user = response.Item ? unmarshall(response.Item) : null;
if (!user) {
return res.status(404).json({ success: false, error: 'User not found' });
}
res.json({ success: true, user });
} catch (error) {
console.error('Error getting user:', error);
res.status(500).json({ success: false, error: 'Failed to get user' });
}
});
// 获取所有用户
app.get('/users', async (req, res) => {
try {
const command = new ScanCommand({ TableName: tableName });
const response = await dynamoDBClient.send(command);
const users = response.Items ? response.Items.map(item => unmarshall(item)) : [];
res.json({ success: true, users });
} catch (error) {
console.error('Error getting users:', error);
res.status(500).json({ success: false, error: 'Failed to get users' });
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});6.3 构建一个 Serverless API
// Lambda 函数:处理 API 请求
const { DynamoDBClient, PutItemCommand, GetItemCommand } = require('@aws-sdk/client-dynamodb');
const { marshall, unmarshall } = require('@aws-sdk/util-dynamodb');
const { v4: uuidv4 } = require('uuid');
const dynamoDBClient = new DynamoDBClient({ region: 'us-east-1' });
const tableName = 'users';
exports.handler = async (event) => {
try {
const { httpMethod, path, body } = event;
// 创建用户
if (httpMethod === 'POST' && path === '/users') {
const user = {
id: uuidv4(),
...JSON.parse(body)
};
const command = new PutItemCommand({
TableName: tableName,
Item: marshall(user)
});
await dynamoDBClient.send(command);
return {
statusCode: 200,
body: JSON.stringify({ success: true, user })
};
}
// 获取用户
if (httpMethod === 'GET' && path.startsWith('/users/')) {
const id = path.split('/')[2];
const command = new GetItemCommand({
TableName: tableName,
Key: marshall({ id })
});
const response = await dynamoDBClient.send(command);
const user = response.Item ? unmarshall(response.Item) : null;
if (!user) {
return {
statusCode: 404,
body: JSON.stringify({ success: false, error: 'User not found' })
};
}
return {
statusCode: 200,
body: JSON.stringify({ success: true, user })
};
}
// 不支持的请求
return {
statusCode: 404,
body: JSON.stringify({ success: false, error: 'Route not found' })
};
} catch (error) {
console.error('Error:', error);
return {
statusCode: 500,
body: JSON.stringify({ success: false, error: 'Internal server error' })
};
}
};7. 总结
AWS SDK for JavaScript 是一个强大的工具包,它允许开发者在 JavaScript 和 Node.js 应用中轻松集成 AWS 服务。通过本教程的学习,你应该已经掌握了 AWS SDK 的核心概念、安装配置、基本使用和高级功能。
AWS SDK 的主要优势包括:
- 全面的服务支持:支持所有 AWS 服务
- 现代 JavaScript 语法:使用 Promise 和 async/await
- 模块化设计:可以只安装需要的服务客户端
- 丰富的功能:包括中间件、分页器、Waiters 等
- 良好的文档:详细的 API 文档和示例代码
通过遵循最佳实践,你可以构建安全、高性能、可靠的 AWS 集成应用。无论是构建 Web 应用、移动应用后端还是 Serverless 架构,AWS SDK 都能为你提供强大的支持。