大文件分段复制实践

需求概要

  由于拷贝的文件比较大,有几G、十几G、几十G单个的文件,并且需要整个目录拷贝,直接用复制粘贴的操作导致内存开销不够,需要建立任务性的拷贝任务,并且把单个文件分段读写。中间有人工中断操作或者被意外中断的可能,下次续传需要接上一次的拷贝进度继续拷贝。

分析功能概要
1.可建立源目录到目标目录的复制任务

2.任务明细包括目录的文件以及子目录递归的所有子文件,并且在目标目录建立相同的子目录结构存放文件。

3.支持操作中断传送任务,以及支持意外中断后续传

4.任务进行中有可视化的任务进度反馈

实现方案

1.基于硬盘之间的拷贝,使用winform桌面应用程序实现功能需求。

界面效果:

2.既然需要记录任务,需要使用数据库记录信息,使用免安装的文件型数据库Sqlite。

3.建立任务表,用于记录拷贝任务;

文件复制任务表:

HT_CopyJob


字段名称


类型


备注说明


HT_ID


Integer


标识


FromDirectoryPath


Varchar


来源目录


ToDirectoryPath


Varchar


目标目录


CreateTime


Datetime


创建时间


FileCount


Integer


总文件数


CopyCount


Integer


当前已复制文件数


LeaveCount


Integer


剩下复制文件数

4.建立任务明细表,使用FileStream缓冲读取和写入,先将流放入内存,再写入文件,每次读取流的位置,要根据上一次最后读取的位置继续读取;记录文件拷贝的信息,以及上一次拷贝的进度,拷贝的情况。

文件复制明细表:

HT_CopyFile


字段名称


类型


备注说明


HT_ID


Integer


标识


CopyJob_ID


Integet


复制任务ID,ht_copyjob.HT_ID


FromPath


Varchar


来源路径


ToPath


Varchar


目标路径


LenCount


Integer


文件总大小


CopyLenCount


Integer


已经复制大小


Position


Integer


记录当前流的位置


IsStart


Integer


是否开始复制0:否,1:开始


IsFinish


Integer


是否完成,0否,1:完成


Msg


Varchar


复制消息

5.创建复制任务主要代码

 if (txt_CopyPath.Text.Trim() == "" || txt_targetPath.Text.Trim() == "")
            {
                MessageBox.Show("请完整选择复制目录和目标目录");
                return;
            }
            List<HT.Model.HT_CopyJobModel> list_cjModel = cjBLL.GetModelList(string.Format(" and FromDirectoryPath=‘{0}‘ and ToDirectoryPath=‘{1}‘ ", txt_CopyPath.Text.Trim(), txt_targetPath.Text.Trim()));
            if (null != list_cjModel && list_cjModel.Count > 0)
            {
                DialogResult result = MessageBox.Show("该相同目录已经存在" + list_cjModel.Count + "个任务,是否还要继续创建?", "警告信息", MessageBoxButtons.YesNoCancel);
                if (result == DialogResult.No || result == DialogResult.Cancel)
                {
                    return;
                }
            }
            HT.Model.HT_CopyJobModel cjModel = new HT.Model.HT_CopyJobModel();
            cjModel.CreateTime = DateTime.Now;
            cjModel.FromDirectoryPath = txt_CopyPath.Text.Trim();
            cjModel.ToDirectoryPath = txt_targetPath.Text.Trim();
            int cjid = cjBLL.Add(cjModel);
            if (cjid > 0)
            {
                int filecount = 0;
                GetAllDirList(cjid, txt_CopyPath.Text.Trim(), txt_CopyPath.Text.Trim(), txt_targetPath.Text.Trim(), ref filecount);
                cjModel = cjBLL.GetModel(cjid);
                cjModel.FileCount = filecount;
                cjModel.LeaveCount = filecount;
                cjModel.CopyCount = 0;
                cjBLL.Update(cjModel);
                // FreeConsole();
                MessageBox.Show("任务创建成功,该任务有" + filecount + "个文件需要复制,请到任务列表开始任务");
            }
            else
            {
                MessageBox.Show("创建任务错误,请重新操作");
            }

递归读取子目录的文件,并且保存到数据库

/// <summary>
        /// 获取子目录以及文件路径保存
        /// </summary>
        /// <param name="cjid">任务ID</param>
        /// <param name="startDir">源路径</param>
        /// <param name="strBaseDir">上一级路径</param>
        /// <param name="targetPath">目标路径</param>
        private void GetAllDirList(int cjid, string startDir, string strBaseDir, string targetPath, ref int fileCount)
        {
            //AllocConsole();
            DirectoryInfo dit = new DirectoryInfo(strBaseDir);
            SaveFilePath(cjid, startDir, dit, targetPath, ref fileCount);
            DirectoryInfo[] list_dit = dit.GetDirectories();
            for (int i = 0; i < list_dit.Length; i++)
            {
                GetAllDirList(cjid, startDir, list_dit[i].FullName, targetPath, ref fileCount);
            }
        }
        /// <summary>
        /// 找到文件夹里的文件,并保存文件路径
        /// </summary>
        /// <param name="cjid">任务ID</param>
        /// <param name="startDir">源路径</param>
        /// <param name="dit">文件夹</param>
        /// <param name="targetPath">目标路径</param>
        private void SaveFilePath(int cjid, string startDir, DirectoryInfo dit, string targetPath, ref int fileCount)
        {
            HT.BLL.HT_CopyFileBLL cfBLL = new HT.BLL.HT_CopyFileBLL();
            HT.Model.HT_CopyFileModel cfModel = null;
            FileInfo[] files = dit.GetFiles();
            if (null != files && files.Count() > 0)
            {
                foreach (var fl in files)
                {
                    try
                    {
                        cfModel = new HT.Model.HT_CopyFileModel();
                        cfModel.CopyJob_ID = cjid;
                        cfModel.FromPath = fl.FullName;
                        cfModel.IsFinish = 0;
                        cfModel.IsStart = 0;
                        cfModel.LenCount = fl.Length + "";
                        cfModel.Msg = "";
                        cfModel.Position = "0";
                        cfModel.ToPath = fl.FullName.Replace(startDir, targetPath);
                        cfModel.CopyLenCount = "0";
                        int cfid = cfBLL.Add(cfModel);
                        if (cfid > 0)
                        {
                            fileCount++;
                            //Console.WriteLine(fl.FullName + " 添加记录成功");
                            richTextBox_Log.Text += fl.FullName + " 添加记录成功\n";
                        }
                        else
                        {
                            //写入错误日志
                        }
                    }
                    catch { }
                }
            }
        }

6.开始执行复制任务主要代码

文件分段读写主要代码:

  //文件复制方法
        private bool CopyFileGo(HT.Model.HT_CopyFileModel cfModel, string fromPath, string toPath, int eachReadLength)
        {
            //将源文件 读取成文件流
            FileStream fromFile = new FileStream(fromPath, FileMode.Open, FileAccess.Read);
            FileInfo fi = new FileInfo(toPath);
            var di = fi.Directory;
            if (!di.Exists)
            {
                di.Create();
            }
            //已追加的方式 写入文件流
            FileStream toFile = new FileStream(toPath, FileMode.Append, FileAccess.Write);
            if (toFile.Length == fromFile.Length)
            {
                cfModel.IsFinish = 1;//已拷贝完成
                cfModel.CopyLenCount = fromFile.Length + "";
                cfBLL.Update(cfModel);
                fromFile.Close();
                toFile.Close();
                return false;
            }
            if (toFile.Length > fromFile.Length)
            {
                cfModel.IsFinish = 2;//拷贝出错了
                cfModel.CopyLenCount = toFile.Length + "";
                cfModel.Msg = "目标文件比源文件大了";
                cfBLL.Update(cfModel);
                fromFile.Close();
                toFile.Close();
                return false;
            }
            //实际读取的文件长度
            int toCopyLength = 0;
            //如果每次读取的长度小于 源文件的长度 分段读取
            if (eachReadLength < fromFile.Length)
            {
                byte[] buffer = new byte[eachReadLength];
                long copied = toFile.Length;
                while (copied <= fromFile.Length - eachReadLength && updateWorker.CancellationPending == false)
                {
                    fromFile.Position = toFile.Length; ;//读之前指定读取的位置
                    toCopyLength = fromFile.Read(buffer, 0, eachReadLength);
                    fromFile.Flush();
                    //toFile.Position = fromFile.Position-eachReadLength;//写之前指定写的位置,等于对完文件后的位置减去读的大小
                    toFile.Write(buffer, 0, eachReadLength);
                    toFile.Flush();
                    copied += toCopyLength;
                    cfModel.CopyLenCount = copied + "";
                    cfModel.Position = fromFile.Position + "";
                    cfBLL.Update(cfModel);
                    progressBarFile.Value += progressBarFile.Step;
                    label_File.Text = "文件(" + cfModel.FromPath + ")进度:" + progressBarFile.Value + "/" + progressBarFile.Maximum;
                    //Console.WriteLine(fromPath + "在分割复制,总共大小为:" + fromFile.Length + "目前进度:" + copied);
                }
                if (updateWorker.CancellationPending)//已取消后台操作
                {
                    fromFile.Close();
                    toFile.Close();
                    return false;
                }
                int left = (int)(fromFile.Length - copied);
                fromFile.Position = toFile.Length;
                toCopyLength = fromFile.Read(buffer, 0, left);
                fromFile.Flush();
                //toFile.Position = fromFile.Position-eachReadLength;
                toFile.Write(buffer, 0, left);
                toFile.Flush();
                cfModel.CopyLenCount = (copied + left) + "";
                cfModel.Position = fromFile.Position + "";
                cfModel.IsFinish = 1;
                cfBLL.Update(cfModel);
                progressBarFile.Value = progressBarFile.Maximum;
                label_File.Text = "文件(" + cfModel.FromPath + ")进度:" + progressBarFile.Value + "/" + progressBarFile.Maximum;
                //Console.WriteLine(fromPath + "总共大小为:" + fromFile.Length + "目前进度:" + copied);
            }
            else
            {
                if (updateWorker.CancellationPending)//已取消后台操作
                {
                    fromFile.Close();
                    toFile.Close();
                    return false;
                }
                //如果每次拷贝的文件长度大于源文件的长度 则将实际文件长度直接拷贝
                byte[] buffer = new byte[fromFile.Length];
                fromFile.Read(buffer, 0, buffer.Length);
                fromFile.Flush();
                toFile.Write(buffer, 0, buffer.Length);
                toFile.Flush();
                cfModel.CopyLenCount = fromFile.Length + "";
                cfModel.Position = fromFile.Position + "";
                cfModel.IsFinish = 1;
                cfBLL.Update(cfModel);
                progressBarFile.Value = progressBarFile.Maximum;
                label_File.Text = "文件(" + cfModel.FromPath + ")进度:" + progressBarFile.Value + "/" + progressBarFile.Maximum;
            }
            fromFile.Close();
            toFile.Close();
            return true;
        }

开始执行复制任务主要代码:

void GoCopyJob(object sender, DoWorkEventArgs e)
        {
            if (cjid == 0)
            {
                MessageBox.Show("请选择执行任务编号");
                return;
            }
            HT.Model.HT_CopyJobModel cjModel = cjBLL.GetModel(cjid);
            if (null != cjModel)
            {
                progressBarJob.Maximum = cjModel.FileCount;
                progressBarJob.Value = cjModel.CopyCount;
                progressBarJob.Step = 1;
                label_Job.Text = "任务(" + cjid + ")进度:" + cjModel.CopyCount + @"/" + cjModel.FileCount;
                List<HT.Model.HT_CopyFileModel> list_cfModel = cfBLL.GetModelList(string.Format(" and CopyJob_ID={0} and (IsStart=0 or IsFinish=0) order by IsStart DESC", cjid));
                if (null != list_cfModel && list_cfModel.Count > 0)
                {
                    foreach (var item in list_cfModel)
                    {
                        if (updateWorker.CancellationPending)
                        { return; }//已取消后台操作
                        progressBarFile.Maximum = int.Parse((long.Parse(item.LenCount) / 1024 / 1024) + "");//转化单位为M
                        progressBarFile.Value = int.Parse((long.Parse(item.CopyLenCount) / 1024 / 1024) + "");
                        progressBarFile.Step = eachReadLength / 1024 / 1024;
                        label_File.Text = "文件(" + item.FromPath + ")进度:" + progressBarFile.Value + "/" + progressBarFile.Maximum;
                        item.IsStart = 1;
                        if (CopyFileGo(item, item.FromPath, item.ToPath, eachReadLength))
                        {
                            //单个文件复制完成,更新界面显示进度
                            cjModel.CopyCount++;
                            cjModel.LeaveCount--;
                            cjBLL.Update(cjModel);
                            if (progressBarJob.Value < progressBarJob.Maximum)
                            {
                                progressBarJob.Value += progressBarJob.Step;
                                label_Job.Text = "任务(" + cjid + ")进度:" + cjModel.CopyCount + @"/" + cjModel.FileCount;
                            }
                        }
                        else
                        {
                            //有文件复制不成功了
                        }
                    }
                    if (progressBarJob.Maximum == progressBarJob.Value)
                    {
                        MessageBox.Show("任务执行完毕");
                    }
                    DataBind();
                }
                else
                {
                    cjModel.CopyCount = cjModel.FileCount;
                    cjModel.LeaveCount = 0;
                    if (cjBLL.Update(cjModel))
                    {
                        MessageBox.Show("已经拷贝完了");
                        DataBind();
                    }
                }
            }
            else
            {
                MessageBox.Show("该任务不存在");
            }
        }

以上就是主要的实现设计与主要的代码了,还有很多未完善和待完善的地方。很少写文章,语言组织上很难讲的明白,还需要以后多写了。欢迎大家指教。

需要源码的欢迎留下联系哦。

时间: 2024-10-08 02:41:01

大文件分段复制实践的相关文章

大文件复制时进行实时保存

1 int main(int argc, const char * argv[]) { 2 @autoreleasepool { 3 4 5 //复制文件 6 7 8 //找到原来的文件路径 9 NSString *soureFilePath = @"/Users/scjy/Downloads/51CTO下载-10大iOS开发者最喜爱的类库.doc"; 10 11 //指定新的文件路径(文件不存在) 12 NSString *objectFilePath = @"/Users

【转】MS SQL 十大最佳存储实践

http://blog.itpub.net/618537/viewspace-683547/?bsh_bid=368266306 [转]MS SQL 十大最佳存储实践,布布扣,bubuko.com

广告行业的大数据处理架构实践

广告行业的大数据处理架构实践 如果您希望阅读更多的大数据机器学习的文章,请关注公众号:QCon大数据机器学习 时间:2015年5月26日 晚20点 讲师介绍:AdMaster技术副总裁,资深大数据技术专家.关注高可靠.高可用.高扩展.高性能系统服务,关注Hadoop/Storm/Spark/ElasticSearch等离线.流式及实时分布式计算技术.曾在联想研究院.百度基础架构部.Carbonite China工作:拥有超过10年云存储.云计算开发及架构工作经验,多年Hadoop实战经验,专注于

MySQL主从延迟复制实践及生产故障案例恢复实践

1.1 MySQL主从延迟复制介绍 从MySQL5.6开始支持了主从延迟复制,这个功能主要解决的问题是,当主库有逻辑的数据删除或错误更新后,所有的从库都会进行错误的更新,从而导致所有的数据库数据异常,即使有定时的备份数据可以用于数据恢复,特别是数据库数据量很大时,恢复时间会很长,再恢复期间数据库数据被删或错误数据影响正常的访问体验. 而延迟复制就可以较好的解决这个问题.例如,可以设定某一个从库和主库的更新延迟1小时,这样主库数据出问题以后,1个小时以内发现,可以对这个从库进行无害恢复处理,使之依

数加平台——阿里大数据OS实践

数加是什么 在阿里云的官网打开大数据部分(整个大数据部分统称为数加),其中包括:大数据基础服务部分,MaxCompute.ADS.流计算.大数据开发套件:人工智能部分,机器学习(基础平台是PAI).语音识别.ET等:数据分析展现部分,数据可视化(大屏.BI报表).I+关系网络分析(安全领域用的比较多):数据应用部分,推荐引擎(提供面向终端用户的服务,以大数据中间件存在)等.天池比赛也是基于数加平台,数加数据市场相当于大数据的App Store. 数加是什么?数加=数加平台+数加市场+数加应用.平

大数据项目实践:基于hadoop+spark+mongodb+mysql开发医院临床知识库系统

一.前言 从20世纪90年代数字化医院概念提出到至今的20多年时间,数字化医院(Digital Hospital)在国内各大医院飞速的普及推广发展,并取得骄人成绩.不但有数字化医院管理信息系统(HIS).影像存档和通信系统(PACS).电子病历系统(EMR)和区域医疗卫生服务(GMIS)等成功实施与普及推广,而且随着日新月异的计算机技术和网络技术的革新,进一步为数字化医院带来新的交互渠道譬如:远程医疗服务,网上挂号预约. 随着IT技术的飞速发展,80%以上的三级医院都相继建立了自己的医院信息系统

java操作大文件复制

1.大文件的复制可以用Java nio中的channel-to-channel传输,Channel-to-channel传输是可以极其快速的,特别是在底层操作系统提供本地支持的时候.某些操作系统可以不必通过用户空间传递数据而进行直接的数据传输.对于大量的数据传输,这会是一个巨大的帮助. 2.代码 package com.dingwang.File; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; i

java 1G大文件复制

对比几种复制方法 复制的文件是980m的txt文件 1.  FileChannel 方法 代码: public static void mappedBuffer() throws IOException{ long start=System.currentTimeMillis(); FileChannel read = new FileInputStream("n2.txt").getChannel(); FileChannel writer = new RandomAccessFil

大数据学习实践总结(2)--环境搭建,JAVA引导,HADOOP搭建

PS:后续的文章会把我实践的内容分解成为一个个的小模块,方便大家的学习,交流.文未我也会附上相关的代码.一起加油!    学有三年的大数据原理,一直没有实践过.最近准备离职,正好把自己所学的大数据内容全部实践一下,也不至于只会纯理论.面对实践,首先要有空杯心态,倒空自己之后,才能学到更多,加油!也希望大家多关注,以后会更多注重实践跟原理的结合. 环境搭建 对于大数据,重点在于Hadoop的底层架构.虽说现在spark架构用的还是比较多.但hadoop还是基础.还有就是为什么要以Linux为基础,