多线程文件分片上传

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程文件分片上传相关的知识,希望对你有一定的参考价值。

文件上传方法

/// <summary>
    /// 上传公共类
    /// </summary>
    public class UploadFile
    {
        #region 变量
        /// <summary>
        /// 起始位置
        /// </summary>
        long Begin;
        /// <summary>
        /// 结束位置
        /// </summary>
        long End;
        /// <summary>
        /// 每次上传的容量
        /// </summary>
        long size;

        /// <summary>
        /// 下载文件的大小
        /// </summary>
        public long FileSize;
        
        public string FileUrl;

        /// <summary>
        /// 是否暂停线程
        /// </summary>
        bool isRunning = true;

        //下载状态
        private UploadStatus status;

        /// <summary>
        /// 每次下载容量
        /// </summary>
        long DownloadCapacity;

        /// <summary>
        /// 下载已耗时
        /// </summary>
        private TimeSpan useTime;

        /// <summary>
        /// 最后一次下载时间
        /// </summary>
        private DateTime lastStartTime;

        /// <summary>
        /// 预计下载总耗时
        /// </summary>
        private TimeSpan allTime;

        /// <summary>
        /// 上一秒时已下载总大小
        /// </summary>
        private long BeforSecondUploadSize;

        /// <summary>
        /// 已下载大小
        /// </summary>
        private long UploadSize = 0;

        /// <summary>
        /// 当前下载速度
        /// </summary>
        private double speed;
        /// <summary>
        /// 是否上传完毕
        /// </summary>
        bool iscomplete = false;

        System.Timers.Timer t = new System.Timers.Timer();

        Task task;
        CancellationTokenSource cancelTokenSource = new CancellationTokenSource();

        public string parentFolderId { get; set; }
        /// <summary>
        /// 任务ID
        /// </summary>
        public string taskID { get; set; }
        /// <summary>
        /// 任务文档ID
        /// </summary>
        public string taskDocId { get; set; }
        /// <summary>
        /// 通用文档树文件夹ID
        /// </summary>
        public string folderId { get; set; }
        /// <summary>
        /// 通用文档根节点ID(第一级目录)
        /// </summary>
        public string docListId { get; set; }
        /// <summary>
        /// 上传或者更新文件类型,0为:作业文档;1为:通用文档;2为:问题文档;3为:工作流文档
        /// </summary>
        public int docType { get; set; }
        /// <summary>
        /// 上传类型,0为:上传;1为:更新
        /// </summary>
        public int opType { get; set; }

        UplpadDetectionFileDto detectionDto = new UplpadDetectionFileDto();
        #endregion

        #region 启动上传
        /// <summary>
        /// 启动上传
        /// </summary>
        /// <param name="UploadUrl">上传的URL</param>
        /// <param name="DetectionUrl">检测文件的URL</param>
        public void Startup(string UploadUrl, string DetectionUrl, UplpadDetectionFileDto DetectionDto)
        {
            if (this.status == UploadStatus.Idle)
            {
                MessageUtil.ShowError("只有空闲中才能上传!");
                return;
            }
            //与webapi通信,传递文件信息
            UplpadDetection Detection = WebCommunication.JsonRequestServiceToObject<UplpadDetection>(DetectionUrl, DetectionDto, CommonInfo.Ticike);
            if (Detection.rsCode == "S10000")
            {
                if (Detection.Isexist == true)
                {
                    Begin = Detection.StartingValue;
                }
                else
                {
                    Begin = 0;
                }
            }
            else
            {
                MessageUtil.ShowError(ExceptionCode.GetCodeByExceptionInfo(Detection.rsCode));
            }

            t.Interval = 1000;
            t.Elapsed -= new System.Timers.ElapsedEventHandler(t_Elapsed);
            t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
            t.Start();
            detectionDto = DetectionDto;
            BeginUpload(UploadUrl, DetectionDto.FileGuid, DetectionDto.ActiomId, DetectionDto.FileName);
        }

        void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            OnUpload();

            if (iscomplete)
            {
                isRunning = false;
                t.Elapsed -= new System.Timers.ElapsedEventHandler(t_Elapsed);
                t.Enabled = false;
                //t.Close();
                UploadSuccessDto Successdto = new UploadSuccessDto();
                Successdto.fileGuid = detectionDto.FileGuid;
                Successdto.fileName = detectionDto.FileName;
                Successdto.proj_id = CommonInfo.ProjectId;
                Successdto.taskID = taskID;
                Successdto.taskDocId = taskDocId;
                Successdto.parentFolderId = parentFolderId;
                Successdto.folderId = folderId;
                Successdto.docListId = docListId;
                Successdto.docType = docType;
                Successdto.opType = opType;
                Successdto.userId = CommonInfo.UserGrid;

                string successUrl = BaseClass.GetUrl("Upload", "UploadSuccess");
                BaseDto dto = WebCommunication.JsonRequestServiceToObject<BaseDto>(successUrl, Successdto, CommonInfo.Ticike);
                if (dto.rsCode == "S10000")
                {
                    string sql = "UPDATE DownloadFileLocally set IsSuccess=1 where ActiomId=‘" + detectionDto.ActiomId + "";
                    if (SQLiteOperation.UpdataSQlite(sql, null) > 0)
                    {
                        DownloadFileService.UpdateFileListComplete(detectionDto.ActiomId);
                        t.Elapsed -= new System.Timers.ElapsedEventHandler(t_Elapsed);
                        uploadComplete(true, "UploadComplete", detectionDto.FileGuid);
                        return;
                    }
                }
                else
                {
                    string errorinfo=ExceptionCode.GetCodeByExceptionInfo(dto.rsCode);
                    MessageUtil.ShowError(detectionDto.FileName + ":" + errorinfo);
                    string sql = "UPDATE DownloadFileLocally set IsSuccess=2,ErrorInfo=‘" + errorinfo + "‘ where ActiomId=‘" + detectionDto.ActiomId + "";
                    SQLiteOperation.UpdataSQlite(sql, null);
                    t.Elapsed -= new System.Timers.ElapsedEventHandler(t_Elapsed);
                    uploadComplete(true, "UploadComplete", detectionDto.FileGuid);
                    return;
                }
            }
            //else { 
            //    if(Begin <= End)
            //}
        } 
        #endregion

        #region 上传暂停
        /// <summary>
        /// 上传暂停
        /// </summary>
        public void Pause()
        {
            if (this.status != UploadStatus.Uploading)
            {
                MessageUtil.ShowError("只有上传中才能暂停!");
                return;
            }

            // 后台线程会查看状态,如果状态时暂停的,
            // 下载将会被暂停并且状态将随之改为暂停.
            this.status = UploadStatus.Pausing;

            isRunning = false;
        } 
        #endregion

        #region 继续上传
        /// <summary>
        /// 继续上传
        /// </summary>
        public void Continue()
        {
            if (this.status != UploadStatus.Pausing)
            {
                MessageUtil.ShowError("只有暂停状态才能继续上传!");
                return;
            }
            this.status = UploadStatus.Uploading;

            isRunning = true;
        } 
        #endregion

        #region 取消上传
        ///<summary>
        ///取消上传
        ///</summary>
        public void Cancel(string FileGuid, string Uploadurl, string ActiomId, string FileName)
        {
            this.status = UploadStatus.Cancel;
            cancelTokenSource.Cancel();
            isRunning = false;
            size = DownloadCapacity * 1024;
            End = FileSize;
            UploadMethod(Uploadurl, DownloadCapacity, FileGuid, ActiomId, FileName, size, End,false);


        } 
        #endregion

        /// <summary>
        /// 重新上传
        /// </summary>
        public void Again(string UploadUrl, string DetectionUrl, UplpadDetectionFileDto DetectionDto)
        {
            this.status = UploadStatus.Cancel;
            cancelTokenSource.Cancel();
            isRunning = false;
            size = DownloadCapacity * 1024;
            End = FileSize;
            UploadMethod(UploadUrl, DownloadCapacity, DetectionDto.FileGuid, DetectionDto.ActiomId, DetectionDto.FileName, size, End, false);

            status = UploadStatus.Uploading;
            UplpadDetection Detection = WebCommunication.JsonRequestServiceToObject<UplpadDetection>(DetectionUrl, DetectionDto, CommonInfo.Ticike);
            if (Detection.rsCode == "S10000")
            {
                if (Detection.Isexist == true)
                {
                    Begin = Detection.StartingValue;
                }
                else
                {
                    Begin = 0;
                }
            }
            else
            {
                MessageUtil.ShowError(ExceptionCode.GetCodeByExceptionInfo(Detection.rsCode));
            }

            t.Interval = 1000;
            t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
            t.Start();
            detectionDto = DetectionDto;
            BeginUpload(UploadUrl, DetectionDto.FileGuid, DetectionDto.ActiomId, DetectionDto.FileName);
        }

        #region 上传方法
        /// <summary>
        /// 多线程上传
        /// </summary>
        /// <param name="UploadUrl">上传URL</param>
        /// <param name="FileGuid">文件Guid</param>
        private void BeginUpload(string UploadUrl,string FileGuid,string ActiomId,string FileName)
        {
            try
            {
                //获取线程数
                EPMSDto.LocalData.DownloadConfiguration configuration = LocalDataDownloadConfiguration.GetDownloadConfigurationByUserName(CommonInfo.LoginName);
                DownloadCapacity = configuration.DownloadCapacity;
                ////计算出单次下载量
                size = DownloadCapacity * 1024;
                End = FileSize;
                bool complete = false;

                //遍历线程数(遍历几次就是启动几个线程)
                for (int i = 0; i < configuration.ThreadNum; i++)
                {
//启动任务 task
= Task.Factory.StartNew(() => UploadMethod(UploadUrl, DownloadCapacity, FileGuid, ActiomId, FileName, size, End, complete), cancelTokenSource.Token); Random rd = new Random(); int num= rd.Next(1500,2000); Thread.Sleep(num); } //UploadMethod(UploadUrl, DownloadCapacity, FileGuid, ActiomId, FileName); } catch (Exception ex) { LogHelper.WriteLog(typeof(DownLoadFile), ex); } } /// <summary> /// 上传方法 /// </summary> /// <param name="Uploadurl">上传的路径</param> /// <param name="DownloadCapacity">每次上传的大小(用户设置的)</param> /// <param name="startingValue"></param> public void UploadMethod(string Uploadurl, long DownloadCapacity, string FileGuid, string ActiomId, string FileName, long size, long End, bool complete) { //if (status != UploadStatus.Uploading) //{ // return; //} if (status == UploadStatus.Cancel) { t.Elapsed -= new System.Timers.ElapsedEventHandler(t_Elapsed); string deletesql = "delete from DownloadFileLocally where ActiomId=‘" + ActiomId + ""; if (SQLiteOperation.UpdataSQlite(deletesql, null) == 0) { MessageUtil.ShowError("删除上传成功的文件记录出错!"); } DownloadFileService.DeleteFileList(ActiomId); WhereDto dt = new WhereDto(); dt.Where = FileGuid; string deleteurl = BaseClass.GetUrl("Upload", "CancelUpload"); BaseDto dto = WebCommunication.JsonRequestServiceToObject<BaseDto>(deleteurl, dt, CommonInfo.Ticike); uploadComplete(true, "UploadCancel", FileGuid); return; } while (End > Begin) { if (Begin < End) { if (!cancelTokenSource.IsCancellationRequested) { //下载保护 if (End - Begin < size) { size = End - Begin; if (size < 0) { size = 0; } //上传完成在这里写 //iscomplete = true; complete = true; } #region 上传

//自旋锁(暂停上传) SpinWait.SpinUntil(() => isRunning); MyUploadFileDto Filedto = new MyUploadFileDto(); BeforSecondUploadSize = size; UploadSize = Begin; //文件信息 System.IO.FileStream readFs = new System.IO.FileStream(FileUrl, System.IO.FileMode.OpenOrCreate, FileAccess.Read, FileShare.ReadWrite); long lenth = readFs.Length; readFs.Position = Begin; byte[] filesize = new byte[size]; readFs.Read(filesize, 0, Convert.ToInt32(size)); readFs.Close(); //MD5加密 Filedto.FileMD5 = PasswordEncrypt.getMD5Hash(filesize); Filedto.ByteInfo = filesize; Filedto.StartingValue = Begin; Filedto.Uploadsize = size; Filedto.Guid = FileGuid; BaseDto dto = WebCommunication.JsonRequestServiceToObject<BaseDto>(Uploadurl, Filedto, CommonInfo.Ticike); if (dto.rsCode != "S10000") { MessageUtil.ShowError(ExceptionCode.GetCodeByExceptionInfo(dto.rsCode)); return; } iscomplete = complete; #endregion } } else { iscomplete = complete; } Begin += size; } if (End <= Begin) { iscomplete = true; } } #endregion #region 每秒信息事件方法 /// <summary> /// 更新下载所用时间 /// </summary> private void ChangeTime() { if (this.status == UploadStatus.Uploading) { DateTime now = DateTime.Now; if (now != lastStartTime) { useTime = useTime.Add(now - lastStartTime); lastStartTime = now; } } } public event UploadEventArgs.SecondUploadEventHandler SecondUploadEvent; /// <summary> /// 每秒信息方法 /// </summary> public void OnUpload() { if (SecondUploadEvent != null) { ChangeTime(); this.speed = (UploadSize - BeforSecondUploadSize) / 1024; long temp = 0; this.allTime = new TimeSpan(temp); int downLoadProportion = 0; if (UploadSize > 0) { downLoadProportion = Convert.ToInt32((Convert.ToDecimal(UploadSize) / Convert.ToDecimal(FileSize)) * 100); } else { downLoadProportion = 0; } UploadEventArgs.SecondUploadEventArgs e = new UploadEventArgs.SecondUploadEventArgs(UploadSize / 1024, useTime, allTime, speed, FileSize / 1024, downLoadProportion); SecondUploadEvent(this, e); } } #endregion #region 上传完成的事件方法 public event UploadEventArgs.UploadCompletedEventHandler UploadCompletedEvent; /// <summary> /// 上传完成的方法 /// </summary> /// <param name="isCompleted"></param> /// <param name="state"></param> /// <param name="Guid"></param> public void uploadComplete(bool isCompleted, string state, string Guid) { if (UploadCompletedEvent != null) { UploadEventArgs.UploadCompletedEvent e = new UploadEventArgs.UploadCompletedEvent(isCompleted, state, Guid); UploadCompletedEvent(this, e); } } #endregion }

 

文件事件委托定义

技术分享
#region 委托类
    public class UploadEventArgs
    {
        /// <summary>
        ///  与每秒下载情况相关的委托
        /// </summary>
        /// <param name="sender">事件发起的对象</param>
        /// <param name="e">参数</param>
        public delegate void SecondUploadEventHandler(Object sender, SecondUploadEventArgs e);
        /// <summary>
        /// 下载完成的委托
        /// </summary>
        /// <param name="sender">事件发起的对象</param>
        /// <param name="e">参数</param>
        public delegate void UploadCompletedEventHandler(Object sender, UploadCompletedEvent e);

        /// <summary>
        /// 下载时每秒事件相关的参数
        /// </summary>
        public class SecondUploadEventArgs : EventArgs
        {
            /// <summary>
            /// 已下载大小
            /// </summary>
            public readonly long uploadSize;

            /// <summary>
            /// 下载已耗时
            /// </summary>
            public readonly TimeSpan useTime;

            //预计下载总耗时
            public readonly TimeSpan allTime;

            /// <summary>
            /// 当前下载速度
            /// </summary>
            public readonly double speed;

            /// <summary>
            /// 文件总大小
            /// </summary>
            public readonly long fileSize;
            /// <summary>
            /// 上传比例
            /// </summary>
            public readonly int uploadProportion;

            public SecondUploadEventArgs(long uploadSize, TimeSpan useTime, TimeSpan allTime, double speed, long fileSize, int uploadproportion)
            {
                this.uploadSize = uploadSize;
                this.useTime = useTime;
                this.allTime = allTime;
                this.speed = speed;
                this.fileSize = fileSize;
                uploadProportion = uploadproportion;
            }
        }

        /// <summary>
        /// 上传完成的
        /// </summary>
        public class UploadCompletedEvent : EventArgs
        {
            /// <summary>
            /// 是否完成
            /// </summary>
            public readonly bool UploadComplete;
            /// <summary>
            /// 上传状态
            /// </summary>
            public readonly string UploadState;

            public readonly string FileGuid;
            public UploadCompletedEvent(bool isComplete, string state, string Guid)
            {
                UploadComplete = isComplete;
                UploadState = state;
                FileGuid = Guid;
            }
        }

    } 
    #endregion

    #region 上传状态枚举
    public enum UploadStatus
    {
        /// <summary>
        /// 下载
        /// </summary>
        Uploading,
        /// <summary>
        /// 
        /// </summary>
        Pausing,
        /// <summary>
        /// 空线
        /// </summary>
        Idle,
        /// <summary>
        /// 取消
        /// </summary>
        Cancel,
        /// <summary>
        /// 重新下载
        /// </summary>
        ReDownload,
        /// <summary>
        /// 完成下载
        /// </summary>
        Complete
    } 
    #endregion
View Code

WebAPI获取文件,保存文件

技术分享
/// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="fileDto"></param>
        /// <returns></returns>
        public static BaseDto UploadFile(MyUploadFileDto fileDto)
        {
            BaseDto dto = new BaseDto();
            //获取到的文件转为MD5码
            string md5code=PasswordEncrypt.getMD5Hash(fileDto.ByteInfo);
            //判断文件转成的MD5码是否与传递来的MD5码一直,不一致则返回异常码信息
            if (md5code != fileDto.FileMD5) {
                dto.rsCode = ErrorExceptionCode.ExceptionCode.UploadFile_MD5Error;
                return dto;
            }
            //获取默认路径
            string Path = System.IO.Path.Combine(FileDownloadHandler.GetWebConfigureDocumentPath("DocumentLocation"));
            //判断默认路径是否存在,不存在则新建
            if (!Directory.Exists(Path))
            {
                Directory.CreateDirectory(Path);
            }
            //组成保存文件的路径
            string address = System.IO.Path.Combine(Path, fileDto.Guid);
            //string address = @"E:\\upload\\" + fileDto.FileName;
            try
            {
                //写入文件
                using (FileStream _fs = new FileStream(address, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite))
                {
                    //文件写入起始位置
                    _fs.Position = fileDto.StartingValue;
                    _fs.Write(fileDto.ByteInfo, 0, (int)fileDto.Uploadsize);
                    _fs.Flush();
                    _fs.Close();
                    _fs.Dispose();
                }
                dto.rsCode = ErrorExceptionCode.ExceptionCode.Success;
            }
            catch (Exception)
            {
                dto.rsCode = ErrorExceptionCode.ExceptionCode.UploadFile_FileSaveError;
            }
            return dto;
        }
View Code

调用上传文件公共类

技术分享
UplpadDetectionFileDto fileDto=new UplpadDetectionFileDto();
            fileDto.FileName=UploadName;
            fileDto.FileGuid = FileGuid;
            fileDto.ActiomId = ActiomId;
            //实例化上传
            //UploadFile file = new UploadFile();
            file.docListId = docListId;
            file.docType = docType;
            file.folderId = folderId;
            file.opType =

以上是关于多线程文件分片上传的主要内容,如果未能解决你的问题,请参考以下文章

PHP大文件分片上传/多线程上传

PHP大文件分片上传/多线程上传

JavaScript大文件分片上传/多线程上传

SpringCloud大文件分片上传/多线程上传

SpringCloud大文件分片上传/多线程上传

Java大文件分片上传/多线程上传解决方案