分享

用 Amazon Web Services 进行云计算,第 2 部分: 用 Amazon ...

 figol 2009-09-27

Amazon Simple Storage Service

本系列的 第 1 部分 介绍了 Amazon Web Services 的构建块,解释了如何使用这个虚拟基础设施构建 Web 范围的系统。

在本文中,学习关于 Amazon Simple Storage Service (S3) 的更多知识。S3 是一个高度可伸缩的快速的 Internet 数据存储系统,用户可以从任何地方在任何时候轻松地存储和获取任意数量的数据。只需根据实际使用的存储和带宽付费。没有安装成本、最低成本或其他间接费用。

Amazon 负责对存储基础设施进行管理和维护,这使您能够把注意力集中在系统和应用程序的核心功能上。S3 是一个具有工业强度的平台,能够轻松地满足您的数据存储需求。它能够:

  • 存储应用程序的数据。

  • 执行个人或企业备份。

  • 把媒体和需要很大带宽的其他内容快速且低成本地分发给您的客户。

S3 的特性包括:

    可靠性
  • 它具有容错能力,能够非常快速地恢复系统,停机时间非常短。Amazon 提供的 服务水平协议 (SLA) 保证 99.99% 的可用性。

  • 简单性
  • S3 基于简单的概念,为开发应用程序提供很强的灵活性。如果需要,可以在 S3 组件之上构建更多功能,从而构建更复杂的存储方案。

  • 可伸缩性
  • S3 提供很强的可伸缩性,可以在出现需求高峰时轻松快速地扩展。

  • 廉价
  • 与市场上的其他企业和个人数据存储解决方案相比,S3 的费率非常有竞争优势。

支撑 S3 框架的三个基本概念是 bucket对象

bucket

bucket 是基本构建块。存储在 Amazon S3 中的每个对象都包含在一个 bucket 中。可以认为 bucket 相当于文件系统上的文件夹(即目录)。文件夹和 bucket 之间的主要差异之一是,每个 bucket 及其内容都可以通过 URL 访问。例如,如果有一个名为 “prabhakar” 的 bucket,就可以使用 URL http://prabhakar.s3. 访问它。

Each S3 账户可以包含最多 100 个 bucket。bucket 不能相互嵌套,所以不能在 bucket 中创建 bucket。在创建 bucket 时,可以指定位置限制,从而影响 bucket 的地理位置。这会自动地确保在这个 bucket 中存储的所有对象都存储在指定的地理位置。目前,可以把 bucket 放在美国或欧盟。如果在创建 bucket 时没有指定位置,那么 bucket 及其内容会存储在最接近您帐户的账单地址的地方。

bucket 名称必须符合下面的 S3 规定:

  • 名称必须以数字或字母开头。

  • 名称的长度必须在 3 到 255 个字符之间。

  • 有效的名称只能包含小写字母、数字、点号、下划线和连字符。

  • 尽管名称可以包含数字和点号,但是不能采用 IP 地址格式。例如,不能把 bucket 命名为 192.168.1.254。

  • bucket 名称空间在 S3 中的所有账户的所有 bucket 之间共享。bucket 名称必须在整个 S3 中是惟一的。

如果要通过 URL 访问 bucket 包含的对象,bucket 名称还必须符合以下规定:

  • bucket 名称必须不包含下划线。

  • 名称的长度必须在 3 到 63 个字符之间。

  • 名称不能以连字符结尾。例如,myfavorite-.bucket.com 是无效的。

  • 在名称中点号的旁边不能有连字符。所以 my-.bucket.com 是无效的。

可以对 bucket 使用域名约定,比如 media.,从而把现有的 Web 域或子域映射到 Amazon S3。通过添加指向 S3 的 DNS CNAME 条目进行实际的映射。这种方案的主要优点是,可以在下载文件的 URL 中使用自己的域名。CNAME 映射负责转换 bucket 的 S3 地址。例如,http://media..s3. 会转换成更友好的 http://media.。

对象

对象 包含存储在 S3 中 bucket 中的数据。可以把对象看作要存储的文件。存储的每个对象由两个实体组成:数据和元数据。数据是要实际存储的东西,比如 PDF 文件、Word 文档、视频文件等。存储的数据还有相关联的元数据,元数据用于描述对象。例如,存储的对象的内容类型、最后一次修改对象的日期以及应用程序特有的其他元数据。在把对象发送给 S3 时,由开发人员以键-值对的形式指定对象的元数据。

S3 对于 bucket 的数量有限制,但是对于对象的数量没有限制。可以在 bucket 中存储任意数量的对象,每个对象可以包含最多 5 GB 数据。

对于可公共访问的 S3 对象,可以使用 HTTP、HTTPS 或 BitTorrent 获取其中的数据。通过使用 BitTorrent,可以非常简便地从 S3 账户分发大型媒体文件;Amazon 不仅会创建对象的 torrent 文件,还会作为它的种子。

在 S3 bucket 中存储的每个对象由一个惟一的键标识。这在概念上与文件系统文件夹中的文件名相似。在硬盘上的文件夹中,文件名必须在文件夹中是惟一的。bucket 中的每个对象必须有且只有一个键。bucket 名称和对象的键共同组成 S3 中存储的对象的惟一标识。

可以使用 URL 访问 S3 中的每个对象,这个 URL 由 S3 服务 URL、bucket 名称和惟一的键组成。如果在名为 prabhakar 的 bucket 中存储一个键为 my_favorite_video.mov 的对象,那么可以使用 URL http://prabhakar.s3./my_favorite_video.mov 访问这个对象。

尽管概念很简单(如图 1 所示),但是 bucket、对象和键共同为构建数据存储解决方案提供很强的灵活性。可以使用这些构建块简便地在 S3 中存储数据,也可以利用其灵活性,在 S3 之上构建更复杂的存储和应用程序,提供更多功能。


图 1. S3 的概念视图





回页首


访问日志记录

每个 S3 bucket 可以有访问日志记录,其中包含每个对象请求的详细信息。在默认情况下,日志记录是关闭的;对于希望跟踪的每个 Amazon S3 bucket,必须显式地启用日志记录。访问日志记录包含关于请求的大量详细信息,包括请求类型、请求的资源和处理请求的日期和时间。

日志采用 S3 Server Access Log Format,但是很容易转换为 Apache Combined Log Format。然后,可以使用任何开放源码或商业日志分析工具(比如 Webalizer)轻松地解析它们,从而提供适合人阅读的报告和漂亮的图表。可以通过报告了解访问文件的客户群。关于可以处理 S3 日志记录的工具的信息见 参考资料





回页首


安全性

在 S3 中创建的每个 bucket 和对象都是创建它们的用户账户私有的。必须显式地把权限授予其他用户和客户,这样他们才能看到 S3 bucket 中的对象列表和下载其中的数据。Amazon S3 提供下面的安全特性,从而保护 bucket 和其中的对象。

    身份验证
  • 确保请求是由拥有 bucket 或对象的用户发出的。每个 S3 请求必须包含惟一地标识用户的 Amazon Web Services 访问键。

  • 授权
  • 确保试图访问资源的用户具有所需的权限。每个 S3 对象有一个相关联的访问控制列表 (ACL),ACL 显式地指定此资源的授权。可以把访问权授予所有 Amazon Web Services 用户,或根据电子邮件地址授予某个用户,还可以把匿名访问权授予任何用户。

  • 完整性
  • 每个 S3 请求必须由发出请求的用户用 Amazon Web Services 密钥进行数字签名。在收到请求时,S3 会检查签名以确保请求没有在传输过程中被篡改。

  • 加密
  • 可以通过 HTTPS 协议访问 S3,从而确保通过加密的连接传输数据。

  • 不可否认
  • 每个 S3 请求都包含时间戳,以此作为事务的证据。

对 S3 的每个 REST 请求必须经历下面的标准步骤以确保安全性:

  • 必须把请求和所需的所有参数组合为一个字符串。

  • 必须使用 Amazon Web Services 密钥创建请求字符串的 Hash Message Authentication Code (HMAC) 签名散列值。

  • 把这个计算出的签名本身作为参数添加到请求中。

  • 然后把请求发给 Amazon S3。

  • Amazon S3 检查提供的签名是否是请求的有效 HMAC 散列值。

  • 只有在签名有效时,Amazon S3 才会处理这个请求。





回页首


Amazon Web Services 和 S3 入门

要想开始使用 S3,需要注册一个 Amazon Web Services 账户。您会得到一个 Amazon Web Services 账号、安全访问密钥以及 x.509 安全证书,在开始使用各种库和工具与 S3 通信时需要这些密钥和安全证书。

与任何 Amazon Web Services 的所有通信都要通过 SOAP 接口或查询/REST 接口。通过这两个接口发送的请求消息必须由发送请求的用户进行数字签名,从而确保消息没有在传输过程中被篡改,并确保它们确实来自发送请求的用户。这是 Amazon Web Services API 使用过程的最基本部分。每个请求必须 进行数字签名并把签名附加到请求上。

每个 Amazon Web Services 用户账户与下面的安全凭证相关联:

  • 访问键 ID,用于识别通过查询/REST 接口发出请求的用户。

  • 安全访问密钥,用于在通过查询接口发出请求时计算数字签名。

  • 公共和私有 x.509 安全证书,用于在使用 SOAP 时进行签名和身份验证。

Web Services Account information 页面上,可以管理密钥和证书、重新生成它们、查看账户活动和使用情况报告以及修改个人信息。

在成功地注册 Amazon Web Services 账户之后,需要按照以下步骤为账户启用 Amazon S3 服务:

  1. 登录 Amazon Web Services 账户。

  2. 导航到 S3 主页

  3. 单击页面右边的 Sign Up For This Web Service

  4. 提供必需的信息并完成注册过程。

本文中的示例使用查询/REST 接口与 S3 通信。需要获得自己的访问键。可以通过在 Web Services Account information 页面上选择 View Access Key Identifiers 获得访问键。现在设置 Amazon Web Services 并为账户启用 S3 服务。





回页首


与 S3 交互

为了学习与 S3 交互,可以使用 Amazon、第三方或独立开发人员提供的库。本文并不深入讨论与 S3 的通信的细节,比如如何对请求进行签名、如何构建用来封装数据的 XML 文档以及对 S3 发送和接收的参数。我们使用库提供的高层接口,让库处理所有细节。更多信息请查阅 S3 开发人员指南

我们将使用开放源码的 Java? 库 JetS3t 与 S3 交互,通过一些代码片段了解它的 API。在本文末尾,将把这些片段组合起来,实现一些有意义的功能:一个简单方便的 S3 shell,可以随时使用它与 S3 交互。

JetS3t

JetS3t 是一个用于与 S3 交互的开放源码 Java 工具箱。它不仅仅是一个库。它的发行版包含几个非常有用的与 S3 相关的工具,一般 S3 用户和在 S3 之上构建应用程序的服务提供商都可以使用它们。JetS3t 包含:

    Cockpit
  • 用于管理 Amazon S3 账户内容的 GUI。

  • Synchronize
  • 用于同步用户计算机上的目录和 Amazon S3 帐户的命令行工具。

  • Gatekeeper
  • 一个 servlet,可以作为访问 Amazon S3 帐户的中介。

  • CockpitLite
  • Cockpit 的轻量版本,它通过中介 gatekeeper 服务路由它的所有操作。

  • Uploader
  • 一个 GUI,它通过中介 gatekeeper 服务路由它的所有操作。服务提供商可以使用它允许客户访问他们的 S3 帐户。

下载 JetS3t 的最新版本。
当然,可以使用这些 GUI 应用程序之一与 S3 交互,但是这对于开发与 S3 交互的应用程序没什么帮助。可以 下载 本文的完整源代码,这是一个压缩的存档文件,其中包含一个准备就绪的 Netbeans 项目,可以把它导入自己的工作空间。

连接 S3

JetS3t 提供一个名为 org.jets3t.service.S3Service 的抽象类,实现特定接口(比如 REST 或 SOAP)的类必须扩展它。JetS3t 提供两个用于连接 S3 和与 S3 交互的实现:

  • org.jets3t.service.impl.rest.httpclient.RestS3Service 通过 REST 接口与 S3 通信。

  • org.jets3t.service.impl.soap.axis.SoapS3Service 通过 SOAP 接口使用 Apache Axis 1.4 与 S3 通信。

JetS3t 使用名为 jets3t.properties 的文件配置在与 S3 通信时使用的各种参数。本文中的示例使用发行版附带的默认 jets3t.properties。JetS3t configuration guide 详细解释了这些参数。

在本文中,将使用 RestS3Service 连接 S3。通过以 AWSCredentials 对象的形式提供 Amazon Web Services 访问键,可以创建一个新的 RestS3Service 对象。请记住,本文中的代码片段只用于演示 API 的使用方法。要想运行这些片段,必须导入所需的所有类。正确的导入语句请参考 下载 包中的源代码。更简单的方法是,把提供的 Netbeans 项目导入自己的工作空间,这样就能够轻松地访问所有源代码。


清单 1. 创建一个新的 RestS3Service
String awsAccessKey = ”Your AWS access key”;
            String awsSecretKey = “Your AWS Secret key”;
            // use your AWS keys to create a credentials object
            AWSCredentials awsCredentials = new AWSCredentials(awsAccessKey, awsSecretKey);
            // create the service object with our AWS credentials
            S3Service s3Service = new RestS3Service(awsCredentials);

管理 bucket

bucket 的概念由 org.jets3t.service.model.S3Bucket 封装,这个类扩展 org.jets3t.service.model.BaseS3Object 类。这个类是 JetS3t 模型中 bucket 和对象的父类。除了各种访问器方法之外,每个 S3Bucket 对象还提供一个 toString() 方法,可以使用它输出 bucket 的重要信息(bucket 的名称和地理位置,创建 bucket 的日期,所有者的姓名,与这个 bucket 相关联的所有元数据)。


清单 2. 列出 bucket
// list all buckets in the AWS account and print info for each bucket.
            S3Bucket[] buckets = s3Service.listAllBuckets();
            for (S3Bucket b : buckets) {
            System.out.println(b);
            }

可以通过提供惟一的 bucket 名称创建新的 bucket。bucket 的名称空间由所有用户账户共享,所以有时候找到惟一的名称可能有困难。还可以指定希望把 bucket 及其包含的对象放在哪里。


清单 3. 创建 bucket
// create a US bucket and print its info
            S3Bucket bucket = s3Service.createBucket(bucketName);
            System.out.println("Created bucket - " + bucketName + " - " + bucket);
            // create a EU bucket and print its info
            S3Bucket bucket = s3Service.createBucket(bucketName, S3Bucket.LOCATION_EUROPE);
            System.out.println("Created bucket - " + bucketName + " - " + bucket);

在删除 bucket 之前,必须删除其中包含的所有对象,否则会引发异常。RestS3Service 类适合处理单一对象。如果需要处理多个对象,最好考虑使用多线程方式以提高速度。JetS3t 提供用于此目的的 org.jets3t.service.multithread.S3ServiceSimpleMulti 类。可以使用这个类包装现有的 s3Service 对象,就可以充分利用多处理器的能力。这对于删除 bucket 中的所有对象很方便。


清单 4. 删除 bucket
// get the bucket
            S3Bucket bucket = getBucketFromName(s3Service, “my bucket”);
            // delete a bucket – it must be empty first
            s3Service.deleteBucket(bucket);
            // create a multi threaded version of the RestService
            S3ServiceSimpleMulti s3ServiceMulti = new S3ServiceSimpleMulti(s3Service);
            // get all the objects from bucket
            S3Object[] objects = s3Service.listObjects(bucket);
            // clear the bucket by deleting all its objects
            s3ServiceMulti.deleteObjects(bucket, objects);

每个 bucket 与一个 ACL 相关联,ACL 决定 bucket 的授权和提供给其他用户的访问级别。可以获取 ACL 并输出它提供的授权。


清单 5. 获取 bucket 的 ACL
// get the bucket
            S3Bucket bucket = getBucketFromName(s3Service, “my bucket”);
            // get the ACL and print it
            AccessControlList acl = s3Service.getBucketAcl(bucket);
            System.out.println(acl);

在新创建的 bucket 和对象上,默认权限指定它们是所有者私有的。可以修改 bucket 的 ACL,向一组用户授予读和写权限,或者对 bucket 的完全控制权。


清单 6. 公开 bucket 及其内容
// get the bucket
            S3Bucket bucket = getBucketFromName(s3Service, “my bucket”);
            // get the ACL
            AccessControlList acl = s3Service.getBucketAcl(bucket);
            // give everyone read access
            acl.grantPermission(GroupGrantee.ALL_USERS, Permission.PERMISSION_READ);
            // save changes back to S3
            bucket.setAcl(acl);
            s3Service.putBucketAcl(bucket);

很容易为 bucket 启用日志记录和获取当前的日志记录状态。启用日志记录之后,会在 S3 中存储这个 bucket 中每个文件的详细访问日志。您的 S3 账户要为日志占用的存储空间付费。


清单 7. S3 bucket 的日志记录
// get the bucket
            S3Bucket bucket = getBucketFromName(s3Service, “my bucket”);
            // is logging enabled?
            S3BucketLoggingStatus loggingStatus = s3Service.getBucketLoggingStatus(bucketName);
            System.out.println(loggingStatus);
            // enable logging
            S3BucketLoggingStatus newLoggingStatus = new S3BucketLoggingStatus();
            // set a prefix for your log files
            newLoggingStatus.setLogfilePrefix(logFilePrefix);
            // set the target bucket name
            newLoggingStatus.setTargetBucketName(bucketName);
            // give the log_delivery group permissions to read and write from the bucket
            AccessControlList acl = s3Service.getBucketAcl(bucket);
            acl.grantPermission(GroupGrantee.LOG_DELIVERY, Permission.PERMISSION_WRITE);
            acl.grantPermission(GroupGrantee.LOG_DELIVERY, Permission.PERMISSION_READ_ACP);
            bucket.setAcl(acl);
            // save the changed ACL for the bucket to S3
            s3Service.putBucketAcl(bucket);
            // save the changes to the bucket logging
            s3Service.setBucketLoggingStatus(bucketName, newLoggingStatus, true);
            System.out.println("The bucket logging status is now enabled.");

管理对象

bucket 中的每个对象由 org.jets3t.service.model.S3Object 表示。每个 S3Object 对象提供一个 toString() 方法,可以使用它输出对象的重要信息:

  • 键的名称

  • 包含它的 bucket 的名称

  • 最后一次修改对象的日期

  • 与对象相关联的所有元数据

它还提供用来访问对象的各个属性和元数据的方法。

清单 8. 列出对象
// list objects in a bucket.
            S3Object[] objects = s3Service.listObjects(bucket);
            // print out the object details
            if (objects.length == 0) {
            System.out.println("No objects found");
            } else {
            for (S3Object o : objects) {
            System.out.println(o);
            }
            }

可以通过提供要匹配的前缀来筛选获取的对象列表。


清单 9. 筛选对象列表
// list objects matching a prefix.
            S3Object[] filteredObjects = s3Service.listObjects(bucket, “myprefix”, null);
            // print out the object details
            if (filteredObjects.length == 0) {
            System.out.println("No objects found");
            } else {
            for (S3Object o : filteredObjects) {
            System.out.println(o);
            }
            }

每个对象可以有相关联的元数据,比如内容类型、修改的日期等等。还可以把应用程序特有的定制元数据与对象关联起来。


清单 10. 获取对象的元数据
// get the bucket
            S3Bucket bucket = getBucketFromName(s3Service, bucketName);
            // getobjects matching a prefix
            S3Object[] filteredObjects = s3Service.listObjects(bucket, “myprefix”, null);
            if (filteredObjects.length == 0) {
            System.out.println("No matching objects found");
            }else {
            // get the metadata for multiple objects.
            S3Object[] objectsWithHeadDetails = s3ServiceMulti.getObjectsHeads(bucket,
            filteredObjects);
            // print out the metadata
            for (S3Object o : objectsWithHeadDetails) {
            System.out.println(o);
            }
            }

在默认情况下,每个新创建的对象都是私有的。可以使用 JetS3t 生成一个带签名的 URL,其他用户可以使用它下载对象数据。这个 URL 只在特定的时间段内有效,在此之后它自动地过期。对象仍然是私有的,但是可以把 URL 提供给任何人,让他们能够在一段时间内下载对象。


清单 11. 生成用于下载对象的带签名的 URL
// get the bucket
            S3Bucket bucket = getBucketFromName(s3Service, bucketName);
            // how long should this URL be valid?
            int duration = Integer.parseInt(tokens.nextToken());
            Calendar cal = Calendar.getInstance();
            cal.add(Calendar.MINUTE, duration);
            Date expiryDate = cal.getTime();
            // create the signed url
            String url = S3Service.createSignedGetUrl(bucketName, objectKey,
            awsCredentials, expiryDate);
            System.out.println("You can use this public URL to access this file for the next "
            + duration + " min - " + url);

S3 允许 bucket 中的每个对象最多包含 5 GB 数据。如果对象超过这个限制,就需要把对象分割为多个不超过 5 GB 的文件,然后把所有部分上传给 S3。


清单 12. 上传到 S3
// get the bucket
            S3Bucket bucket = getBucketFromName(s3Service, bucketName);
            // create an object with the file data
            File fileData = new File(“/my_file_to_upload”);
            S3Object fileObject = new S3Object(bucket, fileData);
            // put the data on S3
            s3Service.putObject(bucket, fileObject);
            System.out.println("Successfully uploaded object - " + fileObject);

JetS3t 提供一个 DownloadPackage 类,它可以方便地把 S3 对象中的数据与本地文件关联起来,自动地把数据保存到文件中。可以使用这个特性轻松地从 S3 下载对象。


清单 13. 从 S3 下载
// get the bucket
            S3Bucket bucket = getBucketFromName(s3Service, bucketName);
            // get the object
            S3Object fileObject = s3Service.getObject(bucket, fileName);
            // associate a file with the object data
            DownloadPackage[] downloadPackages = new DownloadPackage[1];
            downloadPackages[0] = new DownloadPackage(fileObject,
            new File(fileObject.getKey()));
            // download objects to the associated files
            s3ServiceMulti.downloadObjects(bucket, downloadPackages);
            System.out.println("Successfully retrieved object to current directory");

本节讨论了 JetS3t 工具箱提供的一些基本功能,以及如何使用它们与 S3 交互。关于 S3 服务和 JetS3t 工具箱的更多信息请参见 参考资料

S3 shell

目前为止,我们通过代码片段了解了与 S3 的交互。可以把这些片段组合起来,创建一个可以从命令行运行的简单的 S3 shell 程序。我们将创建一个简单的 Java 程序,它通过参数接收 Amazon Web Services 访问键和密钥,返回一个控制台提示。然后,可以输入一个或几个字母,比如 b(列出 bucket)或 om(列出与特定前缀匹配的对象)。使用这个程序体验 S3 服务。

这个 shell 程序包含一个 main() 方法,它的实现由本文中的代码片段组成。为了节省篇幅,这里没有给出 S3 shell 的代码清单。完整的 S3 shell 源代码和它依赖的文件可以在 下载 中找到。只需执行 devworks-s3.jar 文件,即可运行这个 shell。


清单 14. 运行 S3 shell
java -jar devworks-s3.jar my_aws_access_key my_aws_secret_key

在任何时候在 S3 shell 中输入 h,就会列出支持的命令。


图 2. S3 shell 中的帮助

这个 S3 shell 中已经添加了一些有用的方法。可以在其中添加其他功能,让 shell 更适合您的需求。





回页首


结束语

在本文中,了解了 Amazon S3 服务的一些基本概念。学习了用于与 S3 交互的开放源码库 JetS3t。还学习了如何使用示例代码片段创建一个简单的 S3 shell,让您可以使用命令行轻松地体验 S3 服务。

这个 “用 Amazon Web Services 进行云计算” 系列的 第 3 部分 解释如何使用 Amazon Elastic Compute Cloud (EC2) 在云中运行虚拟服务器。






回页首


下载

描述名字大小下载方法
本文的示例代码devworks-s3.zip2.93MBHTTP
关于下载方法的信息


参考资料

学习

获得产品和技术
  • 下载 JetS3t 和其他工具。

  • 下载 IBM 产品评估版,试用这些来自 DB2?、Lotus?、Rational?、Tivoli? 和 WebSphere? 的应用程序开发工具和中间件产品。


讨论


关于作者

http://pubimage.360doc.com/wz/default.gif

Prabhakar Chaganti 是 Ylastic 的 CTO,这家创业公司正在构建一个对用户的整个 AWS 云计算环境(EC2、S3、SQS 和 SimpleDB)进行体系结构设计、管理和监视的统一界面。他是两本新书 Xen VirtualizationGWT Java AJAX Programming 的作者。他还在 VMware Global Virtual Appliance Challenge 上获得了社区评选的最具创意 Virtual Appliance 奖。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多