多线程文件分片上传
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
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; }
调用上传文件公共类
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 =
以上是关于多线程文件分片上传的主要内容,如果未能解决你的问题,请参考以下文章