发表于: 2021-10-15 23:26:02

1 908


一,今天完成的事情

任务七。

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) 页面获取 APPIdSecretIdSecretKey
       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) 页面获取 APPIdSecretIdSecretKey

       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(credclientConfig);
       // bucket名需包含appid
       String bucketName = "mybucket-1251668577";

       ExecutorService threadPool = Executors.newFixedThreadPool(4);
       // 传入一个threadpool, 若不传入线程池默认TransferManager中会生成一个单线程的线程池。
       TransferManager transferManager = new TransferManager(cosclientthreadPool);

       // 设置要下载的对象的前缀(相当于cos上的一个目录),如果设置成 "",则下载整个 bucket
       String cos_path = "/prefix";
       // 要保存下载的文件的文件夹的绝对路径
       String dir_path = "/to/mydir";

       try {
// 返回一个异步结果download, 可同步的调用waitForUploadResult等待download结束.
           MultipleFileDownload download = transferManager.downloadDirectory(bucketNamecos_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) 页面获取 APPIdSecretIdSecretKey
       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(credclientConfig);


       ExecutorService threadPool = Executors.newFixedThreadPool(32);
       // 传入一个threadpool, 若不传入线程池默认TransferManager中会生成一个单线程的线程池。
       TransferManager transferManager = new TransferManager(cosclientthreadPool);

       // 要拷贝的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(srcBucketRegionsrcBucketName,
               srcKeydestBucketNamedestKey);
       try {
Copy copy = transferManager.copy(copyObjectRequestsrcCOSClient, 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) 页面获取 APPIdSecretIdSecretKey
       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(credclientConfig);

       ClientConfig srcClientConfig = new ClientConfig(new Region("ap-shanghai"));
       COSClient srcCosclient = new COSClient(credsrcClientConfig);

       ExecutorService threadPool = Executors.newFixedThreadPool(2);
       // 传入一个threadpool, 若不传入线程池默认TransferManager中会生成一个单线程的线程池。
       TransferManager transferManager = new TransferManager(cosclientthreadPool);
       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<StringString> userMeta = new HashMap<StringString>();
       userMeta.put("usermeta""hello-mult-copy");
       objectMetadata.setUserMetadata(userMeta);

       CopyObjectRequest copyObjectRequest = new CopyObjectRequest(srcBucketRegionsrcBucketName,
               srcKeydestBucketNamedestKey);
       System.out.println(copyObjectRequest.getDestinationBucketName());
       copyObjectRequest.setNewObjectMetadata(objectMetadata);
       try {
Copy copy = transferManager.copy(copyObjectRequestsrcCosclient, 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() {

}       



二,今天问题

数据迁移是一个方向,虽然可能交给运维,知道一些也许能举一反三。


三,今天的收获

现在数据迁移的各种方案,我看的是文字,注意事项很多。狭义的迁移当时用的脚本代码。计算机本科教育,教材往往使用多语言,甚至伪代码,面广,是很有必要。

先下载到本地,再上传是一种思路。怎么做是根据实际规划,再变成代码。


四,明天的计划

任务七补充。任务七深度思考。任务七任务总结。



返回列表 返回列表
评论

    分享到