JAVA 整合 AWS S3(Amazon Simple Storage Service)文件上传,分片上传,删除,下载

依赖

因为aws需要发送请求上传、下载等api,所以需要加上httpclient相关的依赖

<dependency><groupId>com.amazonaws</groupId><artifactId>aws-java-sdk-s3</artifactId><version>1.11.628</version>
</dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version>
</dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpcore</artifactId><version>4.4.13</version>
</dependency>

工具类

上传和下载方式都被重载了,所以可以根据不同的业务场景去使用不同的重载方式。

import com.amazonaws.AmazonServiceException;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.Protocol;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
import com.amazonaws.services.s3.transfer.TransferManager;
import com.amazonaws.services.s3.transfer.TransferManagerBuilder;
import com.amazonaws.services.s3.transfer.Upload;
import com.amazonaws.util.StringUtils;
import com.mocha.order.enums.PropertiesEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;@Component
public class CosUtil {private static final Logger LOGGER = LoggerFactory.getLogger(CosUtil.class);//访问keypublic static String accessKey;//密钥keypublic static String secretKey;//服务区域public static String serviceEndpoint; // e.g., "cos.gz-tst.cos.tg.unicom.local"//区域public static String region; // e.g., "gz-tst"static AWSCredentials credentials ;static AWSStaticCredentialsProvider awsStaticCredentialsProvider ;static ClientConfiguration config ;static AwsClientBuilder.EndpointConfiguration endpointConfiguration ;static AmazonS3 conn ;//分片大小5M,一般设置5的倍数即可,分片大小最大不要超过100Mpublic static final int  FIVE_PARTSIZE=5242880;//1Mpublic static final int  ONE_PARTSIZE=1048576;@PostConstructpublic void init() {//这些配置的值,可以从数据库获取、也可以丛配置文件获取final String cosAccessKey = PropertiesEnum.getCosAccessKey();final String cosSecretKey = PropertiesEnum.getCosSecretKey();final String cosServiceEndpoint = PropertiesEnum.getCosServiceEndpoint();final String cosRegion = PropertiesEnum.getCosRegion();if (org.apache.commons.lang3.StringUtils.isBlank(cosAccessKey)){LOGGER.error("联通云Cos配置的AccessKey为空,请保证【COS_ACCESSKEY】值不为空,否则会影响项目相关功能的使用");}if (org.apache.commons.lang3.StringUtils.isBlank(cosSecretKey)){LOGGER.error("联通云Cos配置的SecretKey为空,请保证【COS_SECRETKEY】值不为空,否则会影响项目相关功能的使用");}if (org.apache.commons.lang3.StringUtils.isBlank(cosServiceEndpoint)){LOGGER.error("联通云Cos配置的ServiceEndpoint为空,请保证【COS_SERVICEENDPOINT】值不为空,否则会影响项目相关功能的使用");}if (org.apache.commons.lang3.StringUtils.isBlank(cosRegion)){LOGGER.error("联通云Cos配置的Region为空,请保证【COS_REGION】值不为空,否则会影响项目相关功能的使用");}LOGGER.info("联通云Cos的配置分别为,accessKey:{},secretKey:{},serviceEndpoint:{},region:{}",cosAccessKey,cosAccessKey,cosServiceEndpoint,cosRegion);LOGGER.info("开始初始化联通云Cos的配置,开始初始化时间:{}",new Date());accessKey=cosAccessKey;secretKey=cosSecretKey;serviceEndpoint=cosServiceEndpoint;region=cosRegion;LOGGER.info("结束初始化联通云Cos的配置,结束初始化时间:{}",new Date());credentials = new BasicAWSCredentials(accessKey, secretKey);awsStaticCredentialsProvider = new AWSStaticCredentialsProvider(credentials);config = new ClientConfiguration();endpointConfiguration = new AwsClientBuilder.EndpointConfiguration(serviceEndpoint, region);conn = AmazonS3ClientBuilder.standard().withCredentials(awsStaticCredentialsProvider).withClientConfiguration(config.withProtocol(Protocol.HTTP).withSignerOverride("S3SignerType")).withEndpointConfiguration(endpointConfiguration).build();}//检查桶是否存在public static boolean doesBucketExist(String bucketName){return conn.listBuckets().stream().map(Bucket::getName).collect(Collectors.toList()).contains(bucketName);}/*** 创建桶* 1、创建已经存在的桶,不会把之前存在的桶内容删除**/public static Bucket createBucket(String bucketName){LOGGER.info("创建桶:{}",bucketName);return conn.createBucket(bucketName);}// 列出所有桶列表public static List<Bucket> listBuckets(){return conn.listBuckets();}// 列出桶内对象public static ObjectListing listObjects(String bucketName){ObjectListing objects = conn.listObjects(bucketName);do {for (S3ObjectSummary objectSummary : objects.getObjectSummaries()) {LOGGER.info(objectSummary.getKey() + "\t" + objectSummary.getSize() + "\t" + StringUtils.fromDate(objectSummary.getLastModified()));}objects = conn.listNextBatchOfObjects(objects);} while (objects.isTruncated());return objects;}//  获取文件public static ObjectMetadata  getObjectMeta(String bucketName, String fileName){LOGGER.info("上传文件,桶名称:{},文件名称:{}",bucketName,fileName);return conn.getObjectMetadata(bucketName, fileName);}// 上传文件-通过实体类public static PutObjectResult uploadFile(PutObjectRequest putObjectRequest){LOGGER.info("上传文件桶实体类方式,实体类是:{}",putObjectRequest);return conn.putObject(putObjectRequest);}// 上传文件-通过Filepublic static PutObjectResult uploadFile(String bucketName, String keyName, File file){LOGGER.info("上传文件,桶名称:{},文件名称:{}",bucketName,keyName);return conn.putObject(bucketName, keyName, file);}// 上传文件-通过InputStreampublic static PutObjectResult uploadFile(String bucketName, String keyName, InputStream inputStream){LOGGER.info("上传文件,桶名称:{},文件名称:{}",bucketName,keyName);return conn.putObject(bucketName, keyName, inputStream, new ObjectMetadata());}// 上传文件-通过字符串public static PutObjectResult uploadFile(String bucketName, String keyName, String content){LOGGER.info("上传文件,桶名称:{},文件名称:{}",bucketName,keyName);return conn.putObject(bucketName, keyName, content);}// 分片上传(不要需要指定分片的大小、还提供上传进度跟踪、断点续传、并发上传等功能)public static Upload uploadFileByShard(String bucketName, String keyName, String filePath) throws InterruptedException,AmazonServiceException {TransferManager tm = TransferManagerBuilder.standard().withS3Client(conn).build();try {LOGGER.info("开始分片上传,桶名称:{},文件名称:{},路径:{}",bucketName,keyName,filePath);Upload upload = tm.upload(bucketName, keyName, new File(filePath));upload.waitForCompletion();LOGGER.info("结束分片上传,桶名称:{},文件名称:{},路径:{}",bucketName,keyName,filePath);return upload;} catch (AmazonServiceException | InterruptedException e) {LOGGER.error("分片上传异常,桶名称:{},文件名称:{},路径:{}",bucketName,keyName,filePath);e.printStackTrace();throw e;}}/*** 大文件分段上传(需要指定分片的大小,自定义的线程池)** @param bucketName  bucketName* @param objectName  objectName* @param file        MultipartFile* @param minPartSize 每片大小,单位:字节(eg:5242880 <- 5m)* @return*/public static boolean uploadMultipartFileByPart(String bucketName, String objectName, File file, int minPartSize) {long size = file.length();final String fileName = file.getName();if (size==0) {LOGGER.error("分片上传的文件:{}为空",fileName);return false;}// 计算分片大小// 得到总共的段数,和 分段后,每个段的开始上传的字节位置List<Long> positions = Collections.synchronizedList(new ArrayList<>());long filePosition = 0;while (filePosition < size) {positions.add(filePosition);filePosition += Math.min(minPartSize, (size - filePosition));}if (LOGGER.isDebugEnabled()) {LOGGER.info("文件:{},总大小:{}字节,分为{}段",fileName, size, positions.size());}// 创建一个列表保存所有分传的 PartETag, 在分段完成后会用到List<PartETag> partETags = Collections.synchronizedList(new ArrayList<>());// 第一步,初始化,声明下面将有一个 Multipart Upload// 设置文件类型ObjectMetadata metadata = new ObjectMetadata();String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);metadata.setContentType(fileType);InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(bucketName, objectName, metadata);InitiateMultipartUploadResult initResponse = conn.initiateMultipartUpload(initRequest);if (LOGGER.isDebugEnabled()) {LOGGER.info("分片上传开始,上传的文件时:{}",fileName);}//声明线程池ExecutorService exec = Executors.newFixedThreadPool(5);long begin = System.currentTimeMillis();try {for (int i = 0; i < positions.size(); i++) {int finalI = i;exec.execute(() -> {long time1 = System.currentTimeMillis();UploadPartRequest uploadRequest = new UploadPartRequest().withBucketName(bucketName).withKey(objectName).withUploadId(initResponse.getUploadId()).withPartNumber(finalI + 1).withFileOffset(positions.get(finalI)).withFile(file).withPartSize(Math.min(minPartSize, (size - positions.get(finalI))));// 第二步,上传分段,并把当前段的 PartETag 放到列表中partETags.add(conn.uploadPart(uploadRequest).getPartETag());LOGGER.info("分片上传的文件时:{},第{}段上传耗时:{}",fileName ,finalI + 1, (System.currentTimeMillis() - time1));});}//任务结束关闭线程池exec.shutdown();//判断线程池是否结束,不加会直接结束方法while (true) {if (exec.isTerminated()) {break;}}// 第三步,完成上传,合并分段CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(bucketName, objectName, initResponse.getUploadId(), partETags);conn.completeMultipartUpload(compRequest);} catch (Exception e) {conn.abortMultipartUpload(new AbortMultipartUploadRequest(bucketName, objectName, initResponse.getUploadId()));LOGGER.error("分片上传文件:{}异常,异常是:{}, ",fileName, e.getLocalizedMessage());e.printStackTrace();}LOGGER.info("分片上传文件:{}结束,总上传耗时:{}毫秒",fileName, (System.currentTimeMillis() - begin));return false;}/*** 初始化,声明有一个Multipart Upload** @param initRequest 初始化请求* @return 初始化返回*/private InitiateMultipartUploadResult initiateMultipartUpload(InitiateMultipartUploadRequest initRequest) {return conn.initiateMultipartUpload(initRequest);}/*** 上传分段** @param uploadRequest 上传请求* @return 上传分段返回* @see <a href="http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/UploadPart">AWS* API Documentation</a>*/private UploadPartResult uploadPart(UploadPartRequest uploadRequest) {return conn.uploadPart(uploadRequest);}/*** 分段合并** @param compRequest 合并请求* @see <a href="http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CompleteMultipartUpload">AWS* API Documentation</a>*/private CompleteMultipartUploadResult completeMultipartUpload(CompleteMultipartUploadRequest compRequest) {return conn.completeMultipartUpload(compRequest);}/*** 中止分片上传** @param uploadRequest 中止文件上传请求* @see <a href="http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/AbortMultipartUpload">AWS* API Documentation</a>*/private void abortMultipartUpload(AbortMultipartUploadRequest uploadRequest) {conn.abortMultipartUpload(uploadRequest);}// 修改对象的访问控制权限public static void modifyFileAccessAuthority(String bucketName, String keyName, CannedAccessControlList cannedAccessControlList){LOGGER.info("修改文件的访问权限,桶名称:{},文件名称:{},权限类型时:{}",bucketName,keyName,cannedAccessControlList.toString());conn.setObjectAcl(bucketName, keyName, cannedAccessControlList);}// 下载一个对象(到指定路径)public static ObjectMetadata downloadFileAssignPath(String bucketName, String keyName, String fileSavePath){return conn.getObject(new GetObjectRequest(bucketName, keyName), new File(fileSavePath));}// 生成对象下载链接(带签名)public static URL generatorFileUrl(String bucketName, String keyName){GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, keyName);return conn.generatePresignedUrl(request);}// 删除文件public static void deleteFie(String bucketName, String keyName){LOGGER.info("删除文件,桶名称:{},文件名称:{},删除时间:{}",bucketName,keyName,new Date());conn.deleteObject(bucketName, keyName);}// 删除桶public static void deleteFie(String bucketName){LOGGER.info("删除桶,桶名称:{},删除时间:{}",bucketName,new Date());conn.deleteBucket(bucketName);}public static void main(String[] args) {String filePath = "G:\\mk\\GDrepo\\repo\\org\\apache\\httpcomponents\\httpcomponents-core\\4.0.1\\httpcomponents-core-4.0.1.pom"; //文件路径//        ByteArrayInputStream input1 = new ByteArrayInputStream("Hello World!".getBytes());
//        CosUtil.createBucket("aaa");
//        final FileInputStream inputStream;
//        try {
//            inputStream = new FileInputStream("G:\\mk\\GDrepo\\repo\\org\\apache\\httpcomponents\\httpcomponents-core\\4.0.1\\httpcomponents-core-4.0.1.pom");
//        } catch (FileNotFoundException e) {
//            throw new RuntimeException(e);
//        }
//        CosUtil.uploadFile("aaa","a.pom",inputStream);CosUtil.uploadFile("test","a.txt",new File(filePath));CosUtil.uploadFile("test","a.txt",new File(filePath));//上传空文件夹conn.putObject("aaa", "demo" + "/","");//上传文件到指定的文件夹final PutObjectRequest putObjectRequest = new PutObjectRequest("aaa", "demo" + "/" + "a.txt", new File(filePath));conn.putObject(putObjectRequest);// 列出所有桶列表List<Bucket> buckets = conn.listBuckets();for (Bucket bucket : buckets) {System.out.println(bucket.getName() + "\t" + StringUtils.fromDate(bucket.getCreationDate()));}// 创建桶Bucket bucket = conn.createBucket("111");Bucket bucket1 = conn.createBucket("111");Bucket bucket12 = conn.createBucket("111");System.out.println(bucket.getName());// 列出桶内对象ObjectListing objects = conn.listObjects(bucket.getName());do {for (S3ObjectSummary objectSummary : objects.getObjectSummaries()) {System.out.println(objectSummary.getKey() + "\t" +objectSummary.getSize() + "\t" +StringUtils.fromDate(objectSummary.getLastModified()));}objects = conn.listNextBatchOfObjects(objects);} while (objects.isTruncated());// 创建对象ByteArrayInputStream input = new ByteArrayInputStream("Hello World!".getBytes());conn.putObject(bucket.getName(), "hello.txt", input, new ObjectMetadata());//分段上传String keyName = "demoya"; //文件上传成功后的文件名TransferManager tm = TransferManagerBuilder.standard().withS3Client(conn).build();try {// TransferManager processes all transfers asynchronously,// so this call returns immediately.System.out.println("开始分片上传");Upload upload = tm.upload("my-test-bucket", keyName, new File(filePath));//Optionally, wait for the upload to finish before continuing.upload.waitForCompletion();System.out.println("结束分片上传");} catch (AmazonServiceException | InterruptedException e) {// The call was transmitted successfully, but Amazon S3 couldn't process// it, so it returned an error response.e.printStackTrace();}// 修改对象的访问控制权限conn.setObjectAcl(bucket.getName(), "hello.txt", CannedAccessControlList.PublicRead);// 下载一个对象(到本地文件)conn.getObject(new GetObjectRequest(bucket.getName(), "hello.txt"), new File("G:\\mk\\hello.txt"));// 生成对象下载链接(带签名)GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucket.getName(), "hello.txt");System.out.println(conn.generatePresignedUrl(request));// 删除一个对象conn.deleteObject(bucket.getName(), "hello.txt");//删除桶conn.deleteBucket(bucket.getName());}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/175791.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker Nginx容器部署vue项目

Docker Nginx容器部署vue项目 文章目录 Docker Nginx容器部署vue项目1. 前提2. 下载nginx镜像3. 编写nginx.conf配置文件4. 编写构建命令5. vue项目上传 1. 前提 Docker服务已部署 2. 下载nginx镜像 首先查看有没有nginx镜像 docker images没有的情况下再进行下载 docker …

Proto3语法详解02

目录 1.默认值 2.更新消息 2.1更新规则 2.2保留字段reserved 2.2.1创建通讯录3.0版本---验证错误删除字段造成的数据损坏 2.3未知字段 2.3.1未知字段从哪获取 3.3.2升级通讯录3.1版本--验证未知字段 2.4前后兼容性 3.选项option 3.1选项分类 3.2常用选项列举 1.默认值…

Python之基础语法和六大数据类型

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

907. 子数组的最小值之和 --力扣 --JAVA

题目 给定一个整数数组 arr&#xff0c;找到 min(b) 的总和&#xff0c;其中 b 的范围为 arr 的每个&#xff08;连续&#xff09;子数组。 由于答案可能很大&#xff0c;因此 返回答案模 10^9 7 。 解题思路 找到以当前值为最小值所能组成的子数组&#xff1b;若存在两个相同…

app分发平台的费用详细过程

在APP分发平台上进行应用商店收费时&#xff0c;通常包括以下费用&#xff1a; 应用审核费用&#xff1a;为了确保应用的质量和用户体验&#xff0c;应用商店会对上传的应用进行审核&#xff0c;审核费用通常是一次性的。应用推广费用&#xff1a;应用商店为了帮助开发者和发布…

pg truncate

命令选项 TRUNCATE [ TABLE ] [ ONLY ] name [ * ] [, ... ][ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]1.ONLY:只truncate指定的表。当表有继承子表或有子分区时&#xff0c;默认会一起truncate;only可只truncate继承父表。分区父表不能指定only --不…

电机伺服驱动学习笔记(7)待编辑

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤1.引入库2.读入数据 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a;…

vue2使用ts vue-class-component

目前&#xff0c;对于Vue3来说&#xff0c;TypeScript的支持已经相当成熟&#xff0c;但公司的老项目一直处于迭代和维护无法从v2重构成v3&#xff0c;并且重构的成本也是很大的一个问题&#xff0c;所以记录一下vue2如何去搭配TypeScript。 目录 一、脚手架创建项目 二、vu…

西南科技大学C++程序设计实验一(C++基础知识)

目录 一、实验目的 二、实验任务 三、预习内容(复习书中前3章内容,说明C++相对于C的扩展有哪些?) 四、问题思考与讨论 一、实验目的 1.熟悉编程环境 2.掌握程序调试方法。 3.熟悉枚举类型、结构体类型等自定义数据类型的使用 4.熟悉函数的定义、说明与使用 5.熟悉引用…

在vue项目中使用vue-video-player播放m3u8视频文件

1.简介 Vue Video Player 是一个基于Vue.js 的视频播放器库&#xff0c;官方API Video.js API docs m3u8是UTF-8编码格式。M3U8是指UTF-8编码的M3U文件&#xff0c;而M3U文件是记录了一个索引纯文本文件&#xff0c;打开它时播放软件并不是播放它&#xff0c;而是根据它的索引…

计算机网络:快速了解网络框架

文章目录 前言一、什么是Internet&#xff1f;1.从具体构成角度什么是协议&#xff1f; 2.从服务角度3小结 二、网络边缘1.采用网络设施面向连接服务&#xff08;TCP&#xff09;2.采用基础设施的无连接服务&#xff08;UDP&#xff09; 三、网络的核心1.电路交换2.分组交换3.分…

时间数据与字符串间相互转换

1. 字符串转成时间 使用datetime.strptime from datetime import datetime start_date "2023-11-28" start_datetime datetime.strptime(start_date, "%Y-%m-%d") print(start_date,start_date) print(start_datetime,start_datetime) print(type(star…

Android : 获取、添加、手机联系人-ContentResolver简单应用

示例图&#xff1a; MainActivity.java package com.example.mygetdata;import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat;import android.Mani…

什么是关系型数据库?

什么是关系型数据库&#xff1f; 关系型数据库&#xff08;RDBMS&#xff09;是建立在关系模型基础上的数据库系统。关系模型是一种数据模型&#xff0c;它表示数据之间的联系&#xff0c;包括一对一、一对多和多对多的关系。在关系型数据库中&#xff0c;数据以表格的形式存储…

Vue项目上线后关闭chroma的vue-devtools调试工具

在项目的main文件中添加如下代码&#xff1a; // 通过设置环境变量 注释生产环境的debug devtools 生产提示 const isDebugMode process.env.NODE_ENV ! production; Vue.config.debug isDebugMode; Vue.config.devtools isDebugMode; Vue.config.productionTip isDebugM…

2024 年应该使用 Bun、Node.js 还是 Deno

2024 年应该使用 Bun、Node.js 还是 Deno 到 2024 年&#xff0c;构建基于 JavaScript 的现代 API 相对简单。我们可以使用Express.js等库并在几分钟内启动可用的 API。但是&#xff0c;现在最具挑战性的部分是选择正确的 JavaScript 引擎。 目前主流的三个运行时是&#xff…

Logstash 部署

目录 1.安装配置Logstash &#xff08;1&#xff09;安装 &#xff08;2&#xff09;测试文件 &#xff08;3&#xff09;配置 服务器 安装软件主机名IP地址系统版本配置LogstashElk10.3.145.14centos7.5.18042核4G 软件版本&#xff1a;logstash-7.13.2.tar.gz 1.安装配…

Android 单元测试初体验(二)-断言

[TOC](Android 单元测试初体验(二)-断言) 前言 当初在学校学安卓的时候&#xff0c;老师敢教学进度&#xff0c;翻到单元测试这一章节的时候提了两句&#xff0c;没有把单元测试当重点讲&#xff0c;只是说我们工作中几乎不会用到&#xff0c;果真在之前的几年工作当中我真的没…

95.STL-遍历算法 for_each

算法概述: 算法主要是由头文件 <algorithm> <functional> <numeric> 组成。 <algorithm> 是所有STL头文件中最大的一个&#xff0c;范围涉及到比较、 交换、查找、遍历操作、复制、修改等等 <numeric> 体积很小&#xff0c;只包括几个在序列上面…

第1章 爬虫基础

目录 1. HTTP 基本原理1.1 URI 和 URL1.2 HTTP 和 HTTPS1.3 请求1.3.1 请求方法1.3.2 请求的网址1.3.3 请求头1.3.4 请求体 1.4 响应1.4.1 响应状态码1.4.2 响应头1.4.3 响应体 2. Web 网页基础2.1 网页的组成2.1.1 HTML2.1.2 CSS2.1.3 JavaScript 2.2 网页的结构2.3 节点树及节…