发表于: 2021-10-15 23:26:02
1 910
一,今天完成的事情
任务七。
1,任务7的要求中说明:
5.编写图片迁移程序:假设是从金山云迁到七牛云,以及从七牛云迁到金山云。
3.能够将数据做正常迁移。
我用的是阿里云的腾讯云。
2,这些要求都可以归结为“数据迁移”,缩小范围是“图片”,“从A云到B云”,云的厂家不同。
同一种云,不同账号,不同bucket(云中常用概念,我理解就是仓库名)之间,复制粘贴往往有比较完善的方法。
有的云在界面的操作就提供数据库复制服务,公有云上有“云迁移”这项目服务,直接从云到云迁移。写自己的代码,可以参考了解别人的策略,所以也可能提供SDK和API。
我看到有人使用代码做任务要求思路是,先把文件下载到本地,再从本地上传到云。这个思路可以考虑。先达到要求,再想着优化。
注意到要求是图片本身,这些不适合将数据库倒出为sql文件的数据。
并且,我是假设 转入和转出,在迁移图片的时候,都可以不使用被迁移的图片。图片在系统中是离线状态。我将图片一次迁移。
之前我没有说,流,java流相关知识可以看。
这个不是图片风格迁移,不是迁移学习,所以不是深度学习。
3,图片也是一种文件。
每种语言都有更适应的地方。迁移我觉得和脚本接近,所以考虑脚本语言。通过搜索,发现和迁移有关的脚本,Python最热门,shell也比较热门。Python更适合我,如果做算法题的时候Python也有很多方法是不能在面试的时候用的,面试官会告知。所以就写Python。
如果直接复制粘贴,先考虑如何从A复制,再粘贴到B。再考虑如何从B复制,再粘贴到A。其中可能还涉及文件中的文件夹遍历的情况。
4,如果是java,参考copy的写法
// 拷贝小文件有以下几种拷贝形式。
// 简单拷贝
// 以下代码用于通过简单拷贝将srcexamplebucket中的srcexampleobject.txt
// 文件拷贝到目标存储空间desexamplebucket中的desexampleobject.txt文件。
private void simpleCopy() {
// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
String endpoint = "yourEndpoint";
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
String accessKeyId = "yourAccessKeyId";
String accessKeySecret = "yourAccessKeySecret";
// 填写源Bucket名称。
String sourceBucketName = "srcexamplebucket";
// 填写源Object的完整路径。Object完整路径中不能包含Bucket名称。
String sourceKey = "srcexampleobject.txt";
// 填写与源Bucket处于同一地域的目标Bucket名称。
String destinationBucketName = "desexamplebucket";
// 填写目标Object的完整路径。Object完整路径中不能包含Bucket名称。
String destinationKey = "desexampleobject.txt";
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 拷贝文件。
CopyObjectResult result = ossClient.copyObject(sourceBucketName, sourceKey,
destinationBucketName, destinationKey);
System.out.println("ETag: " + result.getETag() + " LastModified: " + result.getLastModified());
// 关闭OSSClient。
ossClient.shutdown();
}
一直往下找到。do开头责任链模式。这段代码的风格有很多值得借鉴的地方
protected <T> T doOperation(RequestMessage request, ResponseParser<T> parser, String bucketName, String key, boolean keepResponseOpen, List<RequestHandler> requestHandlers, List<ResponseHandler> reponseHandlers) throws OSSException, ClientException {
WebServiceRequest originalRequest = request.getOriginalRequest();
request.getHeaders().putAll(this.client.getClientConfiguration().getDefaultHeaders());
request.getHeaders().putAll(originalRequest.getHeaders());
request.getParameters().putAll(originalRequest.getParameters());
ExecutionContext context = this.createDefaultContext(request.getMethod(), bucketName, key);
if (context.getCredentials().useSecurityToken() && !request.isUseUrlSignature()) {
request.addHeader("x-oss-security-token", context.getCredentials().getSecurityToken());
}
context.addRequestHandler(new RequestProgressHanlder());
Iterator i$;
if (requestHandlers != null) {
i$ = requestHandlers.iterator();
while(i$.hasNext()) {
RequestHandler handler = (RequestHandler)i$.next();
context.addRequestHandler(handler);
}
}
if (this.client.getClientConfiguration().isCrcCheckEnabled()) {
context.addRequestHandler(new RequestChecksumHanlder());
}
context.addResponseHandler(new ResponseProgressHandler(originalRequest));
if (reponseHandlers != null) {
i$ = reponseHandlers.iterator();
while(i$.hasNext()) {
ResponseHandler handler = (ResponseHandler)i$.next();
context.addResponseHandler(handler);
}
}
if (this.client.getClientConfiguration().isCrcCheckEnabled()) {
context.addResponseHandler(new ResponseChecksumHandler());
}
List<RequestSigner> signerHandlers = this.client.getClientConfiguration().getSignerHandlers();
if (signerHandlers != null) {
Iterator i$ = signerHandlers.iterator();
while(i$.hasNext()) {
RequestSigner signer = (RequestSigner)i$.next();
context.addSignerHandler(signer);
}
}
ResponseMessage response = this.send(request, context, keepResponseOpen);
try {
return parser.parse(response);
} catch (ResponseParseException var14) {
OSSException oe = ExceptionFactory.createInvalidResponseException(response.getRequestId(), var14.getMessage(), var14);
LogUtils.logException("Unable to parse response error: ", var14);
throw oe;
}
}
对于copy这件事,我看到阿里云OSS的流式下载
//以下代码用于流式下载examplebucket中的exampleobject.txt文件。
private void streamDownload() throws IOException {
// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
String endpoint = "yourEndpoint";
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
String accessKeyId = "yourAccessKeyId";
String accessKeySecret = "yourAccessKeySecret";
// 填写Bucket名称。
String bucketName = "examplebucket";
// 填写Object的完整路径。Object完整路径中不能包含Bucket名称。
String objectName = "exampleobject.txt";
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
OSSObject ossObject = ossClient.getObject(bucketName, objectName);
// 读取文件内容。
System.out.println("Object content:");
BufferedReader reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));
while (true) {
String line = reader.readLine();
if (line == null) break;
System.out.println("\n" + line);
}
// 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。
reader.close();
// 关闭OSSClient。
ossClient.shutdown();
}
看到
new BufferedReader(new InputStreamReader(ossObject.getObjectContent()))
我知道阿里云的ossObject被包裹,看下去
public InputStream getObjectContent() {
return this.objectContent;
}
找到InputStream。如果用java的流,不用把A云的文件下载下来再上传到B云了。也是因为现在我测试用的文件不大,用缓存能接住。就算磁盘IO不那么快,先把A云的存到磁盘,再上传到B云完成数据迁移,是大文件的时候很容易想到的办法。
5,回头来看python的流式下载。
# -*- coding: utf-8 -*-
import oss2
# 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
auth = oss2.Auth('yourAccessKeyId', 'yourAccessKeySecret')
# yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
# 填写Bucket名称。
bucket = oss2.Bucket(auth, 'yourEndpoint', 'examplebucket')
# bucket.get_object的返回值是一个类文件对象(File-Like Object),同时也是一个可迭代对象(Iterable)。
# 填写Object的完整路径。Object完整路径中不能包含Bucket名称。
object_stream = bucket.get_object('exampleobject.txt')
print(object_stream.read())
# 由于get_object接口返回的是一个stream流,需要执行read()后才能计算出返回Object数据的CRC checksum,因此需要在调用该接口后进行CRC校验。
if object_stream.client_crc != object_stream.server_crc:
print("The CRC checksum between client and server is inconsistent!")
所以OSS中Python如何拿到流也知道了
6,腾讯云测试迁移。阿里云到腾讯云。
代码可以很简单。以下就可以做到。阿里云的OSS和腾讯云的OSS设计理念非常相似,导致有的类非常相似。所以需要用以下两种区分
com.aliyun.oss.model.ObjectMetadata
com.qcloud.cos.model.ObjectMetadata
public static void fromOssToCos(OSSClient ossClient, COSClient cosClient, String objectKey) {
OSSObject ossObject = ossClient.getObject(aliyun_bucketName, objectKey);
InputStream in = ossObject.getObjectContent();
com.aliyun.oss.model.ObjectMetadata metaGet = ossObject.getObjectMetadata();
Map<String, String> metaDataGet = metaGet.getUserMetadata();
com.qcloud.cos.model.ObjectMetadata metaPut = new com.qcloud.cos.model.ObjectMetadata();
Map<String, String> metaDataPut = new HashMap<>();
metaDataPut.putAll(metaDataGet);
metaPut.setUserMetadata(metaDataPut);
cosClient.putObject(tencent_bucketName,objectKey,in,metaPut);
}
但是,我推荐分成以下模块。甚至能提供异步的上传文件, 下载文件,copy文件的功能。可以根据文件大小自动的选择上传接口或者copy接口。文件夹中上传遇到文件夹,需要上传就递归。
public class Transfer {
// Prints progress while waiting for the transfer to finish.
private static void showTransferProgress(Transfer transfer) {
}
// 上传文件, 根据文件大小自动选择简单上传或者分块上传。
public static void uploadFile() {
}
// 上传文件(上传过程中暂停, 并继续上传)
public static void pauseUploadFileAndResume() {
}
public static void multipartUploadWithMetaData() {
}
// 批量上传,上传文件夹,需要提供需要上传文件夹的绝对路径。可以递归
public static void uploadDirectory() {
// 1 初始化用户身份信息(secretId, secretKey)
// 请先在访问管理控制台中的 [API 密钥管理](https://console.cloud.tencent.com/cam/capi) 页面获取 APPId、SecretId、SecretKey
COSCredentials cred = new BasicCOSCredentials("yourSecretId", "yourSecretKey");
// 2 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224
ClientConfig clientConfig = new ClientConfig(new Region("ap-beijing"));
// 3 生成cos客户端
COSClient cosclient = new COSClient(cred, clientConfig);
// bucket名需包含appid
String bucketName = "mybucket-1251668577";
ExecutorService threadPool = Executors.newFixedThreadPool(4);
// 传入一个threadpool, 若不传入线程池, 默认TransferManager中会生成一个单线程的线程池。
TransferManager transferManager = new TransferManager(cosclient, threadPool);
// 设置文件上传到 bucket 之后的前缀目录,设置为 “”,表示上传到 bucket 的根目录
String cos_path = "/prefix";
// 要上传的文件夹的绝对路径
String dir_path = "/to/mydir";
// 是否递归上传目录下的子目录,如果是 true,子目录下的文件也会上传,且cos上会保持目录结构
Boolean recursive = false;
try {
// 返回一个异步结果Upload, 可同步的调用waitForUploadResult等待upload结束, 成功返回UploadResult, 失败抛出异常.
MultipleFileUpload upload = transferManager.uploadDirectory(bucketName, cos_path, new File(dir_path), recursive);
// 可以选择查看上传进度
showTransferProgress(upload);
// 或者阻塞等待完成
upload.waitForCompletion();
System.out.println("upload directory done.");
} catch (CosServiceException e) {
e.printStackTrace();
} catch (CosClientException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
transferManager.shutdownNow();
cosclient.shutdown();
}
// 批量下载,下载文件夹。可以递归
public static void downloadDirectory() {
// 1 初始化用户身份信息(secretId, secretKey)
// 请先在访问管理控制台中的 [API 密钥管理](https://console.cloud.tencent.com/cam/capi) 页面获取 APPId、SecretId、SecretKeyCOSCredentials cred = new BasicCOSCredentials("yourSecretId", "yourSecretKey");
// 2 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224
ClientConfig clientConfig = new ClientConfig(new Region("ap-beijing"));
// 3 生成cos客户端
COSClient cosclient = new COSClient(cred, clientConfig);
// bucket名需包含appid
String bucketName = "mybucket-1251668577";
ExecutorService threadPool = Executors.newFixedThreadPool(4);
// 传入一个threadpool, 若不传入线程池, 默认TransferManager中会生成一个单线程的线程池。
TransferManager transferManager = new TransferManager(cosclient, threadPool);
// 设置要下载的对象的前缀(相当于cos上的一个目录),如果设置成 "",则下载整个 bucket。
String cos_path = "/prefix";
// 要保存下载的文件的文件夹的绝对路径
String dir_path = "/to/mydir";
try {
// 返回一个异步结果download, 可同步的调用waitForUploadResult等待download结束.
MultipleFileDownload download = transferManager.downloadDirectory(bucketName, cos_path, new File(dir_path));
// 可以选择查看下载进度
showTransferProgress(download);
// 或者阻塞等待完成
download.waitForCompletion();
System.out.println("download directory done.");
} catch (CosServiceException e) {
e.printStackTrace();
} catch (CosClientException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
transferManager.shutdownNow();
cosclient.shutdown();
}
// 将文件下载到本地
public static void downLoadFile() {
}
// 将文件下载到本地(中途暂停并继续开始)
public static void pauseDownloadFileAndResume() {
}
// copy接口支持根据文件大小自动选择copy或者分块copy
// 以下代码展示跨园区拷贝, 即将一个园区的文件拷贝到另一个园区
public static void copyFileForDiffRegion() {
// 1 初始化用户身份信息(secretId, secretKey)
// 请先在访问管理控制台中的 [API 密钥管理](https://console.cloud.tencent.com/cam/capi) 页面获取 APPId、SecretId、SecretKey
COSCredentials cred = new BasicCOSCredentials("yourSecretId", "yourSecretKey");
// 2 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224
ClientConfig clientConfig = new ClientConfig(new Region("ap-beijing"));
// 3 生成cos客户端
COSClient cosclient = new COSClient(cred, clientConfig);
ExecutorService threadPool = Executors.newFixedThreadPool(32);
// 传入一个threadpool, 若不传入线程池, 默认TransferManager中会生成一个单线程的线程池。
TransferManager transferManager = new TransferManager(cosclient, threadPool);
// 要拷贝的bucket region, 支持跨园区拷贝
Region srcBucketRegion = new Region("ap-shanghai");
// 源bucket, bucket名需包含appid
String srcBucketName = "srcBucket-1251668577";
// 要拷贝的源文件
String srcKey = "aaa/bbb.txt";
// 目的bucket, bucket名需包含appid
String destBucketName = "destBucket-1251668577";
// 要拷贝的目的文件
String destKey = "ccc/ddd.txt";
COSClient srcCOSClient = new COSClient(cred, new ClientConfig(srcBucketRegion));
CopyObjectRequest copyObjectRequest = new CopyObjectRequest(srcBucketRegion, srcBucketName,
srcKey, destBucketName, destKey);
try {
Copy copy = transferManager.copy(copyObjectRequest, srcCOSClient, null);
// 返回一个异步结果copy, 可同步的调用waitForCopyResult等待copy结束, 成功返回CopyResult, 失败抛出异常.
CopyResult copyResult = copy.waitForCopyResult();
} catch (CosServiceException e) {
e.printStackTrace();
} catch (CosClientException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
transferManager.shutdownNow();
srcCOSClient.shutdown();
cosclient.shutdown();
}
// copy接口支持根据文件大小自动选择copy或者分块copy
// 以下代码展示同园区拷贝, 即将同园区的文件拷贝到另一个园区
public static void copyFileForSameRegion() {
}
public static void copyFileSetMetadata() {
// 1 初始化用户身份信息(secretId, secretKey)
// 请先在访问管理控制台中的 [API 密钥管理](https://console.cloud.tencent.com/cam/capi) 页面获取 APPId、SecretId、SecretKey
COSCredentials cred = new BasicCOSCredentials("yourSecretId", "yourSecretKey");
// 2 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224
ClientConfig clientConfig = new ClientConfig(new Region("ap-guangzhou"));
// 3 生成cos客户端
COSClient cosclient = new COSClient(cred, clientConfig);
ClientConfig srcClientConfig = new ClientConfig(new Region("ap-shanghai"));
COSClient srcCosclient = new COSClient(cred, srcClientConfig);
ExecutorService threadPool = Executors.newFixedThreadPool(2);
// 传入一个threadpool, 若不传入线程池, 默认TransferManager中会生成一个单线程的线程池。
TransferManager transferManager = new TransferManager(cosclient, threadPool);
TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();
transferManagerConfiguration.setMultipartCopyThreshold(5*1024*1024);
transferManager.setConfiguration(transferManagerConfiguration);
// 要拷贝的bucket region, 支持跨园区拷贝
Region srcBucketRegion = new Region("ap-shanghai");
// 源bucket, bucket名需包含appid
String srcBucketName = "mysrcbucket-123456789";
// 要拷贝的源文件
String srcKey = "aaa/bbb.txt";
// 目的bucket, bucket名需包含appid
String destBucketName = "mydestbucekt-123456789";
// 要拷贝的目的文件
String destKey = "bbb/ccc.txt";
ObjectMetadata objectMetadata = new ObjectMetadata();
Map<String, String> userMeta = new HashMap<String, String>();
userMeta.put("usermeta", "hello-mult-copy");
objectMetadata.setUserMetadata(userMeta);
CopyObjectRequest copyObjectRequest = new CopyObjectRequest(srcBucketRegion, srcBucketName,
srcKey, destBucketName, destKey);
System.out.println(copyObjectRequest.getDestinationBucketName());
copyObjectRequest.setNewObjectMetadata(objectMetadata);
try {
Copy copy = transferManager.copy(copyObjectRequest, srcCosclient, null);
// 返回一个异步结果copy, 可同步的调用waitForCopyResult等待copy结束, 成功返回CopyResult, 失败抛出异常.
//showTransferProgress(copy);
CopyResult copyResult = copy.waitForCopyResult();
System.out.println(copyResult.getCrc64Ecma());
} catch (CosServiceException e) {
e.printStackTrace();
} catch (CosClientException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
transferManager.shutdownNow();
cosclient.shutdown();
}
public static void resumableDownloadFile() {
}
二,今天问题
数据迁移是一个方向,虽然可能交给运维,知道一些也许能举一反三。
三,今天的收获
现在数据迁移的各种方案,我看的是文字,注意事项很多。狭义的迁移当时用的脚本代码。计算机本科教育,教材往往使用多语言,甚至伪代码,面广,是很有必要。
先下载到本地,再上传是一种思路。怎么做是根据实际规划,再变成代码。
四,明天的计划
任务七补充。任务七深度思考。任务七任务总结。
评论