Amazon S3 对象存储服务深度解析:存储原理、应用场景与实战指南

Amazon S3 对象存储服务深度解析:存储原理、应用场景与实战指南

Amazon S3对象存储服务,在可扩展性、数据可用性、安全性和能效方面业界领先,数百万不同规模和行业的客户可以为几乎任何应用场景存储、管理、分析和保护任意数量的数据,例如数据湖、云原生应用程序和移动应用程序,借助高成本效益的存储类和易于使用的管理功能,可以优化成本、组织并分析数据,以及配置精细调整过的访问控制,从而满足特定的业务和合规性要求。

亚马逊云科技注册

亚马逊云科技提供众多免费云产品,其中Amazon EC2 云服务器可以免费体验,访问亚马逊云科技官网
1、亚马逊云科技官网账号注册

2、进入注册页面(使用电子邮件地址进行注册)

  • 进入 创建亚马逊云科技账户页面(如果最近登录过 亚马逊云科技,请选择登录控制台。如果未显示创建新亚马逊云科技账户,请首先选择登录其他账户,然后选择创建新亚马逊云科技账户)

  • 根用户电子邮件地址 中,输入电子邮件地址,编辑亚马逊云科技账户名称,然后选择验证电子邮件地址,该地址会收到一封包含验证码的亚马逊云科技验证电子邮件

  • 安全验证
  • 验证电子邮件地址(输入收到的代码,然后选择验证,可能需要几分钟才会收到代码,检查电子邮件和垃圾邮件文件夹中是否有验证码电子邮件)

3、设置用户信息

  • 创建密码:输入根用户密码和确认根用户密码,然后选择继续
  • 添加您的联系信息:选择个人或企业(个人账户和企业账户具有相同的特性和功能)

4、添加付款信息( 账单信息页面上,输入付款方式的信息,然后选择验证并添加,必须先添加有效的支付方式才能继续注册)

5、用户信息验证(选择接收验证码的联系方式、选择电话号码的国家或地区代码)

6、客户验证与激活账户(选择亚马逊云科技支持服务计划)

7、完成注册

Amazon S3工作原理

Amazon S3 将数据作为对象存储在存储桶中。对象是一个文件和描述该文件的任何元数据。存储桶是对象的容器。要将数据存储在 Amazon S3 中,需要先创建一个存储桶并指定存储桶名称和亚马逊云科技区域。然后,将数据作为 Amazon S3 中的对象上传到该存储桶。每个对象都有一个密钥(或密钥名称),它是存储桶中对象的唯一标识符。

S3 提供可供您配置以支持您的特定应用场景的功能。例如,您可以使用 S3 版本控制将对象的多个版本保留在同一个存储桶中,这样就可以恢复意外删除或覆盖的对象。存储桶及其中的对象是私有的,只能在明确授予访问权限的情况下进行访问。您可以使用存储桶策略、Amazon Identity and Access Management(IAM)策略、S3 接入点和访问控制列表(ACL)来管理访问权限。

Amazon S3创建流程

1、Amazon S3创建前提是拥有一个亚马逊云科技账号即可领取Amazon S3服务免费体验

2、首页找到 Amazon S3 服务选项

3、创建存储桶

4、存储桶的常规配置

  • 桶名称
  • 区域
  • 对象所有权
  • 加密设置
  • 高级配置

5、加密类型、存储桶密钥

6、等待存储桶创建成功即可

7、存储桶配置

8、定义新文件夹的名称,根据需求进行相应配置

9、创建成功即可在Amazon S3存储桶中存储和同步数据了

Amazon S3实现网盘文件上传下载功能

基于 Vue.js + Spring Boot + Amazon S3 的网盘系统实现,包含文件上传、下载、列表展示、文件夹管理等

后端实现(Spring Boot + Amazon SDK)
  • application.properties配置

如下application.properties信息需要根据自己的Amazon S3配置信息替换

properties 复制代码
#AmazonS3配置
aws.access-key=key-id          #AmazonS3密钥ID
aws.secret-key=key      #AmazonS3访问密钥
aws.region=region                     #存储桶区域
aws.s3.bucket-name=bucket-name         #存储桶名称

#文件上传配置
spring.servlet.multipart.max-file-size=50MB    # 单个文件最大大小
spring.servlet.multipart.max-request-size=50MB # 单次请求最大大小

#应用配置
spring.application.name=s3-cloud-storage       # 应用名称
server.port=8080                               # 服务器端口
  • S3Config.java

通过 @Configuration 注解标记为配置组件,使用 @Value 从配置文件注入亚马逊云科技凭证、区域和存储桶名称,并通过 @Bean 注解创建两个核心组件:S3Client 用于与 S3 服务交互,采用静态凭证提供器注入凭证;bucketName 作为字符串 Bean 提供存储桶名称,便于在其他组件中注入使用,实现了与 S3 服务的基础连接配置

java 复制代码
package com.example.s3cloud.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;

@Configuration
public class S3Config {

    @Value("${aws.access-key}")
    private String accessKey;

    @Value("${aws.secret-key}")
    private String secretKey;

    @Value("${aws.region}")
    private String region;

    @Value("${aws.s3.bucket-name}")
    private String bucketName;

    @Bean
    public S3Client s3Client() {
        return S3Client.builder()
                .region(Region.of(region))
                .credentialsProvider(StaticCredentialsProvider.create(
                        AwsBasicCredentials.create(accessKey, secretKey)))
                .build();
    }

    @Bean
    public String bucketName() {
        return bucketName;
    }
}
  • S3Service.java

通过 Spring 管理,生成唯一文件键确保路径不冲突,设 ACL=PRIVATE 保证文件私有,伪目录机制模拟文件夹结构,通过预签名 URL 实现安全临时访问,所有操作均自动处理签名和加密传输

java 复制代码
package com.example.s3cloud.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;

import java.io.IOException;
import java.net.URL;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Service
public class S3Service {

    private final S3Client s3Client;
    private final String bucketName;

    @Autowired
    public S3Service(S3Client s3Client, String bucketName) {
        this.s3Client = s3Client;
        this.bucketName = bucketName;
    }

    // 上传文件到S3
    public String uploadFile(String userId, String folderPath, MultipartFile file) throws IOException {
        String fileKey = generateFileKey(userId, folderPath, file.getOriginalFilename());
        PutObjectRequest request = PutObjectRequest.builder()
                .bucket(bucketName)
                .key(fileKey)
                .contentType(file.getContentType())
                .acl(ObjectCannedACL.PRIVATE)
                .build();
        s3Client.putObject(request, RequestBody.fromInputStream(file.getInputStream(), file.getSize()));
        return fileKey;
    }

    // 生成预签名下载URL
    public String generatePresignedUrl(String fileKey, Duration expiration) {
        GetObjectRequest request = GetObjectRequest.builder()
                .bucket(bucketName)
                .key(fileKey)
                .build();
        return s3Client.utilities().generatePresignedUrl(request, expiration).toString();
    }

    // 列出文件夹内容
    public List<S3Object> listFolderContent(String prefix) {
        ListObjectsV2Request request = ListObjectsV2Request.builder()
                .bucket(bucketName)
                .prefix(prefix)
                .delimiter("/")
                .build();
        ListObjectsV2Response response = s3Client.listObjectsV2(request);
        return new ArrayList<>(response.contents());
    }

    // 创建文件夹(S3中通过上传零字节文件实现)
    public void createFolder(String folderKey) {
        PutObjectRequest request = PutObjectRequest.builder()
                .bucket(bucketName)
                .key(folderKey + "/")
                .build();
        s3Client.putObject(request, RequestBody.empty());
    }

    // 删除文件或文件夹
    public void delete(String key) {
        DeleteObjectRequest request = DeleteObjectRequest.builder()
                .bucket(bucketName)
                .key(key)
                .build();
        s3Client.deleteObject(request);
    }

    // 生成唯一文件键
    private String generateFileKey(String userId, String folderPath, String originalFilename) {
        String uuid = UUID.randomUUID().toString();
        return String.format("user/%s/%s/%s-%s", 
                userId, 
                folderPath, 
                uuid, 
                originalFilename);
    }
}
  • FileController.java

提供文件上传、列表查询、生成下载链接和删除接口,依赖 S3Service 实现业务逻辑,接口基于用户 ID 和路径操作 S3,返回统一格式响应并处理异常

java 复制代码
package com.example.s3cloud.controller;

import com.example.s3cloud.model.FileInfo;
import com.example.s3cloud.model.ResponseResult;
import com.example.s3cloud.service.S3Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.services.s3.model.S3Object;

import java.time.Duration;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/files")
public class FileController {

    @Autowired
    private S3Service s3Service;

    // 上传文件
    @PostMapping("/upload")
    public ResponseResult<String> uploadFile(
            @RequestParam("userId") String userId,
            @RequestParam("folderPath") String folderPath,
            @RequestParam("file") MultipartFile file) {
        try {
            String fileKey = s3Service.uploadFile(userId, folderPath, file);
            return ResponseResult.success(fileKey);
        } catch (Exception e) {
            return ResponseResult.error("上传失败: " + e.getMessage());
        }
    }

    // 获取文件夹内容
    @GetMapping("/list")
    public ResponseResult<List<FileInfo>> listFiles(
            @RequestParam("userId") String userId,
            @RequestParam("folderPath") String folderPath) {
        try {
            String prefix = String.format("user/%s/%s", userId, folderPath);
            List<S3Object> objects = s3Service.listFolderContent(prefix);
            
            List<FileInfo> fileInfos = objects.stream()
                    .filter(obj -> !obj.key().endsWith("/")) // 过滤文件夹标记
                    .map(obj -> {
                        String fileName = obj.key().substring(obj.key().lastIndexOf("/") + 1);
                        return new FileInfo(
                                obj.key(),
                                fileName,
                                obj.size(),
                                obj.lastModified().toString()
                        );
                    })
                    .collect(Collectors.toList());
            
            return ResponseResult.success(fileInfos);
        } catch (Exception e) {
            return ResponseResult.error("获取文件列表失败: " + e.getMessage());
        }
    }

    // 生成下载链接
    @GetMapping("/download-url")
    public ResponseResult<String> generateDownloadUrl(@RequestParam("fileKey") String fileKey) {
        try {
            String url = s3Service.generatePresignedUrl(fileKey, Duration.ofHours(1));
            return ResponseResult.success(url);
        } catch (Exception e) {
            return ResponseResult.error("生成下载链接失败: " + e.getMessage());
        }
    }

    // 删除文件
    @DeleteMapping("/delete")
    public ResponseResult<Void> deleteFile(@RequestParam("fileKey") String fileKey) {
        try {
            s3Service.delete(fileKey);
            return ResponseResult.success();
        } catch (Exception e) {
            return ResponseResult.error("删除失败: " + e.getMessage());
        }
    }
}
前端实现(Vue.js + Element UI)
  • FileUploader.vue

文件上传组件,支持拖拽和多文件上传,通过 props 接收用户 ID 和目录路径,动态拼接参数上传至 S3,自带进度显示、错误处理和成功通知

html 复制代码
<template>
  <div class="file-uploader">
    <el-upload
      class="upload-demo"
      drag
      :action="uploadUrl"
      :headers="headers"
      :multiple="true"
      :data="uploadData"
      :on-success="handleUploadSuccess"
      :on-error="handleUploadError"
      :on-progress="handleUploadProgress">
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
      <div class="el-upload__tip" slot="tip">
        支持最大50MB的文件上传,仅允许常见文档、图片、视频格式
      </div>
    </el-upload>
  </div>
</template>

<script>
import { uploadFile } from '@/api/file';

export default {
  props: {
    userId: {
      type: String,
      required: true
    },
    currentFolder: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      uploadUrl: '/api/files/upload',
      uploadData: {},
      progress: 0
    };
  },
  computed: {
    headers() {
      return {
        'Authorization': `Bearer ${localStorage.getItem('token')}`
      };
    }
  },
  methods: {
    handleUploadSuccess(response, file, fileList) {
      this.$message.success('上传成功');
      this.$emit('upload-success');
    },
    handleUploadError(err, file, fileList) {
      this.$message.error('上传失败');
    },
    handleUploadProgress(event, file, fileList) {
      this.progress = Math.round(event.percent);
    }
  },
  watch: {
    userId: {
      immediate: true,
      handler(val) {
        this.uploadData = {
          userId: val,
          folderPath: this.currentFolder
        };
      }
    },
    currentFolder(val) {
      this.uploadData = {
        userId: this.userId,
        folderPath: val
      };
    }
  }
};
</script>
  • FileList.vue

通过 props 接收用户 ID 和当前目录路径,初始化及目录切换时调用接口获取文件列表,以表格形式展示文件名、大小、修改时间,并提供下载和删除操作

html 复制代码
<template>
  <div class="file-list">
    <el-table
      :data="fileList"
      stripe
      border
      @row-click="handleRowClick">
      <el-table-column prop="fileName" label="文件名" min-width="300">
        <template slot-scope="scope">
          <i class="el-icon-document" v-if="!scope.row.isFolder"></i>
          <i class="el-icon-folder" v-else></i>
          <span>{{ scope.row.fileName }}</span>
        </template>
      </el-table-column>
      <el-table-column prop="fileSize" label="大小" width="120">
        <template slot-scope="scope">
          {{ formatSize(scope.row.fileSize) }}
        </template>
      </el-table-column>
      <el-table-column prop="lastModified" label="修改时间" width="180"></el-table-column>
      <el-table-column label="操作" width="200">
        <template slot-scope="scope">
          <el-button
            size="mini"
            @click="handleDownload(scope.row, $event)">
            下载
          </el-button>
          <el-button
            size="mini"
            type="danger"
            @click="handleDelete(scope.row, $event)">
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
import { getFileList, generateDownloadUrl, deleteFile } from '@/api/file';

export default {
  props: {
    userId: {
      type: String,
      required: true
    },
    currentFolder: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      fileList: []
    };
  },
  created() {
    this.fetchFileList();
  },
  watch: {
    currentFolder() {
      this.fetchFileList();
    }
  },
  methods: {
    async fetchFileList() {
      try {
        const res = await getFileList(this.userId, this.currentFolder);
        this.fileList = res.data;
      } catch (error) {
        this.$message.error('获取文件列表失败');
      }
    },
    async handleDownload(file) {
      try {
        const res = await generateDownloadUrl(file.fileKey);
        window.open(res.data);
      } catch (error) {
        this.$message.error('获取下载链接失败');
      }
    },
    async handleDelete(file) {
      this.$confirm('确定要删除此文件吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(async () => {
        try {
          await deleteFile(file.fileKey);
          this.$message.success('删除成功');
          this.fetchFileList();
        } catch (error) {
          this.$message.error('删除失败');
        }
      }).catch(() => {
        // 取消操作
      });
    },
    formatSize(bytes) {
      if (bytes === 0) return '0 B';
      const k = 1024;
      const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }
  }
};
</script>
实现流程

Amazon S3 实现网盘文件上传下载功能时,上传流程为前端将用户选择的文件封装成 FormData 发送至后端接口,后端生成唯一文件键,调用Amazon SDK 将文件以私有权限存入 S3 并返回存储标识;下载流程是前端请求后端生成文件对应预签名 URL,该 URL 携带加密签名且有时间限制,返回后触发浏览器下载;文件列表展示则由前端向后端请求指定用户和目录路径下的文件信息,后端通过 S3 的 listObjectsV2 接口获取数据并处理,将文件名、大小等元数据返回前端渲染展示,实现网盘基础文件管理功能

  • 文件上传流程:前端选择文件后,调用 /api/files/upload 接口后端接收文件,生成唯一 Key,文件上传至 S3,返回存储 Key 给前端
  • 文件下载流程:前端点击下载按钮,调用 /api/files/download-url 接口,后端生成有效期 1 小时的预签名 URL,前端打开 URL 直接下载文件
  • 文件列表展示:前端调用 /api/files/list 接口,传入用户 ID 和文件夹路径,后端通过 S3 的 listObjectsV2 获取文件列表,解析文件元数据(文件名、大小、修改时间)返回给前端

Amazon S3使用场景

Amazon S3 广泛应用于多类场景:构建数据湖支撑大数据分析、AI、ML 及 HPC 应用以挖掘数据见解;借助强大复制功能实现关键数据备份与还原,满足 RTO、RPO 及合规性要求;通过归档至 S3 Glacier 存储类降低成本并支持数据洞察;还能通过 Amazon PrivateLink 实现混合云存储,在 VPC 中预置私有终端节点,以私有 IP 打通本地与 S3 的直接访问亚马逊云科技

  • 企业级数据存储:支撑网盘、文件管理系统等场景,如博主讲述的基于 Vue + Spring Boot 实现的文件上传下载功能,通过用户隔离路径和预签名 URL 保障数据安全
  • 静态资源托管:存储网站图片、CSS、JS 等静态文件,结合 CloudFront CDN 加速全球访问
  • 大数据与分析:作为数据湖存储原始数据,供 EMR、Athena 等服务进行批量处理和实时分析
  • 备份与归档:利用生命周期管理策略自动将低频访问数据归档至 S3 Glacier,降低存储成本
  • 移动与 IoT 数据收集:接收设备日志、用户行为数据等海量流式数据,支持高并发写入和持久化存储

总结

Amazon S3 是高效且灵活的对象存储方案:注册账号后可快速创建存储桶,通过 IAM 实现权限隔离;后端用 Spring Boot 集成 Amazon SDK 完成配置与文件操作(如唯一路径生成、预签名 URL 控制下载时效),前端借助 Vue + Element UI 组件实现交互(拖拽上传、表格展示);利用伪文件夹机制和用户 ID 分层可构建网盘目录,结合版本控制、生命周期管理等功能,能低成本实现数据安全、备份与成本优化,适合企业网盘、静态资源托管、大数据分析等场景,与云原生技术栈集成可显著提升开发运维效率。
友情提示:如果决定不再使用服务的话,记得要在控制台关闭服务,以防超过免费额度产生扣费

相关推荐
观测云1 个月前
观测云,全球领先的监控观测平台亮相亚马逊云科技中国峰会!
亚马逊云科技·观测云
大数据在线2 个月前
当向量数据库与云计算相遇:AI应用全面提速
人工智能·云计算·向量数据库·亚马逊云科技·zilliz
亦世凡华、2 个月前
加速用户体验:Amazon CloudFront 实践与优化技巧
经验分享·教程·亚马逊云科技·cloudfront
大数据在线3 个月前
AI重塑云基础设施,亚马逊云科技打造AI定制版IaaS“样板房”
人工智能·云基础设施·ai大模型·亚马逊云科技
佛州小李哥4 个月前
在云平台上用Claude 3.7 AI代理自动化电脑图形界面点击操作做表格
人工智能·计算机视觉·ai·语言模型·aws·亚马逊云科技·ai代理
指剑4 个月前
AWS Bedrock全托管接入国产大模型DeepSeek-R1[内涵免费使用DeepSeek-R1满血版]
云计算·aws·亚马逊云科技·白嫖·deepseek
佛州小李哥4 个月前
利用亚马逊云科技”多模态AI知识库“获取非结构化数据总结
人工智能·ai·语言模型·云计算·aws·亚马逊云科技·ai代理
cnnews4 个月前
AWS中使用CloudFront分发位于S3中的静态网站
javascript·云计算·aws·cloudfront·amazon s3·route 53
佛州小李哥4 个月前
阿里通义万相2.1模型在亚马逊云科技ECS容器中的私有化部署
人工智能·科技·阿里云·云计算·aws·亚马逊云科技·通义万相