ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

阿里云文件上传工具开发(一)第一版本

2021-11-01 16:31:39  阅读:114  来源: 互联网

标签:文件夹 文件 任务 阿里 版本 var checkpointDir 上传


文章目录

前言

  • 最近小胖接到一个任务,就是公司要求把一些大文件从linux上,上传到阿里云上,但是又不想使用阿里云的工具,想要写个程序在后台跑,定时将文件夹中的没上传过的文件上传到阿里云上。好家伙,听到这里我在想为啥不用工具,它提供的工具不好吗,上传就一个命令而已,没必要吧,但是任务就是任务没办法

需求

  • 开始做任务前,需要确定一下需求
  1. 将文件上传
  2. 定时任务
  3. 自动判断文件夹中是否有未上传的文件
  4. 可自动配置是否删除已上传的文件

第一次尝试

  • 因为第一次做这种程序类的任务,还是有点懵逼的,但是还是顺着需求一步步来,首先先把阿里云上传的公共类调通先。
  • 对于阿里云上传的上传方式都有四种,分别是简单上传,追加上传,断点续传和分片上传,前两个方法都是小文件上传的,后面两个是大文件上传的,一般来说都会选择后面两种,以为一般上传到上面的都不会是小文件。
  • 而在分片上传和断点续传两种,我选择了后者,因为为了避免程序因为一些意外而终止之后,还需要重新上传文件,所以我选择了断点续传。
  • 下面附上阿里云工具类的代码,至于如何使用,看网址阿里云断点续传
using Aliyun.OSS;
using Aliyun.OSS.Common;

// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
var endpoint = "yourEndpoint";
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
var accessKeyId = "yourAccessKeyId";
var accessKeySecret = "yourAccessKeySecret";
// 填写Bucket名称,例如examplebucket。
var bucketName = "examplebucket";
// 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
var objectName = "exampledir/exampleobject.txt";
// 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
// 如果未指定本地路径只填写了文件名称(例如examplefile.txt),则默认从示例程序所属项目对应本地路径中上传文件。
var localFilename = "D:\\localpath\\examplefile.txt";
// 记录本地分片上传结果的文件。上传过程中的进度信息会保存在该文件中。
string checkpointDir = "yourCheckpointDir";
// 创建OssClient实例。
var client = new OssClient(endpoint, accessKeyId, accessKeySecret);
try
{
    // 通过UploadFileRequest设置多个参数。
    UploadObjectRequest request = new UploadObjectRequest(bucketName, objectName, localFilename)
    {
        // 指定上传的分片大小。
        PartSize = 8 * 1024 * 1024,
        // 指定并发线程数。
        ParallelThreadCount = 3,
        // checkpointDir保存断点续传的中间状态,用于失败后继续上传。
        // 如果checkpointDir为null,断点续传功能不会生效,每次失败后都会重新上传。
        CheckpointDir = checkpointDir,
    };
    // 断点续传上传。
    client.ResumableUploadObject(request);
    Console.WriteLine("Resumable upload object:{0} succeeded", objectName);
}
catch (OssException ex)
{
    Console.WriteLine("Failed with error code: {0}; Error info: {1}. \nRequestID:{2}\tHostID:{3}",
        ex.ErrorCode, ex.Message, ex.RequestId, ex.HostId);
}
catch (Exception ex)
{
    Console.WriteLine("Failed with error info: {0}", ex.Message);
}
  • 好了,阿里云的上传工具类调通了,剩下就剩两个重点,一个是配置文件的添加,另一个则是定时任务,还是先看配置文件的添加,java的配置类我还是会配置,但是c#程序后台获取配置类我还是第一次写,哪能咋办的只能去网上找了,一开始我找的是config配置类的获取,但是领导说这是很老的技术了,现在用的都是json的配置类获取,好吧,我也觉得是,所以下面是我找的配置文件获取其中字段的方法
        /// <summary>
        /// 获取配置类里的配置
        /// </summary>
        private static Dictionary<string, string> GetSettings(string jsonFile)
        {
            var map = new Dictionary<string, string>();
            using (System.IO.StreamReader file = System.IO.File.OpenText(@jsonFile))
            {
                using (JsonTextReader reader = new JsonTextReader(file))
                {
                    JObject setting = (JObject)JToken.ReadFrom(reader);
                    foreach(var item in setting)
                    {
                        map.Add(item.Key, item.Value.ToString());
                    }
                }
            }
            return map;
        }
  • 上面是我获取c#的配置文件的方法了,如果有更好的方法,请评论区告诉我,谢谢大家了
  • 好了解决掉获取配置文件的方法,那我们就可以先来着手来写文件上传的流程了,因为我们不仅是一次上传还是多次上传,所以我的第一个想法就是,将阿里云的文件列表拿下来,再把我的本地文件列表获取,然后将两者的文件名相互比对,这样就知道那些文件没有上传,将这些文件路径保存,然后开始循环上传。流程如下:
    • 获取配置类的文件目录
    • 根据文件夹名称去阿里云得到文件夹
    • 根据文件目录等到本地文件名称
    • 文件相互比对,找出未上传的文件
    • 将未上传的文件上传
			// 1. 获取配置类的文件目录
            var settings = GetSettings();
            var logs = new List<string>();
            foreach (var key in settings.Keys)
            {
                if (key.Contains("Log"))
                {
                    logs.Add(settings[key]);
                }
            }
            var updateFiles = new List<string[]>();
            foreach (var log in logs)
            {
                // 2. 根据文件名称去阿里云得到文件名
                var item = log.Split("\\");
                var objectNames = GetObjectNames(endpoint, accessKeyId, accessKeySecret, bucketName, item[item.Length - 1]);
                // 3. 根据文件目录得到本地文件名称
                var files = GetFileNames(log);

                var fileNames = files.Select(d =>
                {
                    var strs = d.Split(log);
                    var str = strs[1].Replace("\\", "/");
                    return new string[]{
                        item[item.Length - 1] + str,
                        d
                    };
                }).ToList();

                // 4. 文件相互比对,找出未上传的文件
                foreach (var fileName in fileNames)
                {
                    if (!objectNames.Contains(fileName[0]))
                    {
                        updateFiles.Add(fileName);
                    }
                }
            }

            // 5. 将未上传的文件上传
            foreach (var updateFile in updateFiles)
            {
                //断点续传临时地址
                var checkpointDir = "D:\\test\\checkpoint_" + DateTime.Now.ToString("yyyy_MM_dd");
                if (!Directory.Exists(checkpointDir))
                {
                    Directory.CreateDirectory(checkpointDir);
                }
                var objectName = updateFile[0];
                var localFilename = updateFile[1];
                PutObject(endpoint, accessKeyId, accessKeySecret, bucketName, objectName, localFilename, checkpointDir);
            }
            // 6. 删除文件

            Console.WriteLine("----------------------");
  • 本来我以为这样就大功告成了,并且佩服我自己的聪明才智,后来领导的评价是,十分粗糙,并且流程有问题,给我提了一个问题,怎么判断这个文件到底是上传成功还是上传失败,并且假设上传到一半终止了,你这个又怎么判断呢,你这样太繁琐了,有没有简便一点的方法来判断是否已经上传呢?
  • 这几个问题一出来,我就知道,我写的真是个垃圾,这种解决方案确实太过繁琐了,但是如何判断文件的上传状态呢?
  • 以我的聪明的脑袋瓜只能想到两种方法
    • 第一种:通过配置文件来存储已经上传和上传中的文件名称,但是这种方法一样繁琐,给否决了
    • 第二种:通过更改文件名称来判断,其实我觉得也挺繁琐的,但是领导思考了一下还是让我想想
  • 最后看到我实在想不出了,领导给了解决方案,创建两个文件夹,其中一个用来存放上传中的文件,叫原文件夹_uploadig,另一个用来存放已经上传成功的文件,叫原文件夹_uploaded。这样就不需要上面这么复杂,并且删除文件的时候直接删除最后一个文件夹里的文件就好了,这想法简直了,我给五星好评。
  • 在没有加上定时任务的时候,因为有了断点续传,所以我觉得中间那个过度的文件夹就没必要了,流程应该是下面这样的:
    • 获取配置信息
    • 将要上传的文件夹中的文件上传
      • 上传完毕
      • 查看是否有uploaded文件夹
        • 如果没有,则创建
        • 如果有,则将上传完毕的文件转移到uploaded文件
      • 看配置信息中是否有需要删除
        • 如果有,则删除
        • 如果没有,则不删除
 				foreach (var project in projects)
                {
                    //项目原本路径
                    var file = project.Value.ToString();
                    //项目上传中路径
                    var uploading = file + "_uploading";
                    //项目上传完毕
                    var uploaded = file + "_uploaded";

                    //需要上传的文件
                    var uploadFiles = new List<string>();
 
                    // 2. 获取未上传的文件并上传,上传结束后将上传文件放入上传完成的目录中
                    //如果不存在就创建file文件夹
                    if (!Directory.Exists(file))
                    {
                        Directory.CreateDirectory(file);
                    }
                    var files = GetFileNames(file);
                    uploadFiles.AddRange(files);

                    // 3. 上传文件,上传结束后将上传的文件放入上传完成的目录中
                    foreach (var uploadFile in uploadFiles)
                    {
                        string objectName = project.Key + "/" + GetFileName(uploadFile);
                        string localFilename = uploadFile;
                        //创建断点续传的临时文件
                        string checkpointDir = Path.Combine(System.Environment.CurrentDirectory, "checkpointDir");
                        //如果不存在就创建file文件夹
                        if (!Directory.Exists(@checkpointDir))
                        {
                            Directory.CreateDirectory(@checkpointDir);
                        }

                        //上传文件
                        var flag = PutObject(endpoint, accessKeyId, accessKeySecret, bucketName, objectName, localFilename, checkpointDir);
                        //将上传完的文件放入到uploaded文件夹中
                        if (flag)
                        {
                            //如果不存在就创建file文件夹
                            if (!Directory.Exists(uploaded))
                            {
                                Directory.CreateDirectory(uploaded);
                            }

                            var uploadedFile = Path.Combine(uploaded, GetFileName(uploadFile));
                            MoveFile(uploadFile, uploadedFile);
                        }
                    }
                    // 4. 判断是否需要删除文件,如果需要则删除文件
                    if (isDelete == "true")
                    {
                        var uploadedFiles = GetFileNames(uploaded);
                        foreach (var uploadedFile in uploadedFiles)
                        {
                            File.Delete(uploadedFile);
                        }
                    }
               }
  • 好了,解决完大部分流程了之后,我就要开始思考定时任务怎么办,定时任务就是在经过一定时间后自动执行想要执行的任务,这个倒是不难,但是又有个问题,假设上一个任务还没有完成,拿下一个任务又开始了怎么办?
  • 这个时候就需要用到锁了,但时间刚好开始定时任务,我们就把这个线程锁住,并且关闭定时任务,等到任务执行完,就打开锁,并且开启任务,这样就可以解决上面的问题了,给出代码
        /// <summary>
        /// 定时任务
        /// </summary>
        public static void TimeTask(int time)
        {
            Console.WriteLine("定时任务开始" + DateTime.Now.ToString() + ":");
            //定时任务
            timer = new System.Timers.Timer(time);
            timer.Enabled = true;
            //执行间隔时间,单位为毫秒;   
            timer.Interval = time; 
            timer.Start();
            timer.Elapsed += new System.Timers.ElapsedEventHandler(TaskLocal);
            timer.AutoReset = true;
        }

        /// <summary>
        /// 任务锁:当上一个任务未执行完之前,并不开启定时任务
        /// </summary>
        public static void TaskLocal(object source, ElapsedEventArgs e)
        {
            //lock (o)
            {
                //先将定时任务取消
                timer.Enabled = false;
                lock (task)
                {
                    task = Convert.ToInt32(task) + 1;
                    Thread.CurrentThread.Name = task.ToString();
                }
                Run();
                //任务运行完,将定时任务重新开启
                timer.Enabled = true;
            }
        }
  • 但是我感觉我这代码有点问题,如果大佬可以给我点改进意见
  • 以上就是我这个任务最初的模型了,我跑了一天,发现他没有报错依旧坚挺,但是这个工具远远不会这么简单,后面领导又提了几个需求,所有我们还需要做第二版。

标签:文件夹,文件,任务,阿里,版本,var,checkpointDir,上传
来源: https://blog.csdn.net/qq_41816516/article/details/121077365

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有