首页 > 代码库 > Html5 js上传插件resumable.js 的使用

Html5 js上传插件resumable.js 的使用

  最近做一个csv文件上传,网上找了几个,兼容性不好,年代也比较久远,就去github上看看找找,发现了一个resumable.js,支持分片上传,多文件上传,完全满足需求~项目地址:https://github.com/23/resumable.js。

  简单说下应用,顺便保存下代码,万一哪天再用了,忘了还得重新找.....这才是关键。

  后台代码,两个文件,一个model,一个webapi,基于C#的。

  model代码:

namespace Resumable.Models{	public class ResumableConfiguration	{		/// <summary>		/// Gets or sets number of expected chunks in this upload.		/// </summary>		public int Chunks { get; set; }		/// <summary>		/// Gets or sets unique identifier for current upload.		/// </summary>		public string Identifier { get; set; }				/// <summary>		/// Gets or sets file name.		/// </summary>		public string FileName { get; set; }		public ResumableConfiguration()		{		}		/// <summary>		/// Creates an object with file upload configuration.		/// </summary>		/// <param name="identifier">Upload unique identifier.</param>		/// <param name="filename">File name.</param>		/// <param name="chunks">Number of file chunks.</param>		/// <returns>File upload configuration.</returns>		public static ResumableConfiguration Create(string identifier, string filename, int chunks)		{			return new ResumableConfiguration { Identifier = identifier, FileName = filename, Chunks = chunks };		}	}}

  API代码:

using Resumable.Models;using System;using System.IO;using System.Net;using System.Net.Http;using System.Threading.Tasks;using System.Web.Http;namespace Resumable.Controllers{	[RoutePrefix("api/File")]	public class FileUploadController : ApiController	{		private string root = System.Web.Hosting.HostingEnvironment.MapPath("~/upload"); 				[Route("Upload"), HttpOptions]		public object UploadFileOptions()		{			return Request.CreateResponse(HttpStatusCode.OK);		}		[Route("Upload"), HttpGet]		public object Upload(int resumableChunkNumber, string resumableIdentifier)		{			return ChunkIsHere(resumableChunkNumber, resumableIdentifier) ? Request.CreateResponse(HttpStatusCode.OK) : Request.CreateResponse(HttpStatusCode.NoContent);		}		[Route("Upload"), HttpPost]		public async Task<object> Upload()		{			// Check if the request contains multipart/form-data.			if (!Request.Content.IsMimeMultipartContent())			{				throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);			}			if (!Directory.Exists(root)) Directory.CreateDirectory(root);			var provider = new MultipartFormDataStreamProvider(root);			if (await readPart(provider))			{				// Success				return Request.CreateResponse(HttpStatusCode.OK);			}			else			{				// Fail				var message = DeleteInvalidChunkData(provider) ? "Cannot read multi part file data." : "Cannot delete temporary file chunk data.";				return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, new System.Exception(message));			}		}		private static bool DeleteInvalidChunkData(MultipartFormDataStreamProvider provider)		{			try			{				var localFileName = provider.FileData[0].LocalFileName;				if (File.Exists(localFileName))				{					File.Delete(localFileName);				}				return true;			}			catch {				return false;			}		}		private async Task<bool> readPart(MultipartFormDataStreamProvider provider)		{			try			{				await Request.Content.ReadAsMultipartAsync(provider);				ResumableConfiguration configuration = GetUploadConfiguration(provider);				int chunkNumber = GetChunkNumber(provider);				// Rename generated file				MultipartFileData chunk = provider.FileData[0]; // Only one file in multipart message				RenameChunk(chunk, chunkNumber, configuration.Identifier);				// Assemble chunks into single file if they‘re all here				TryAssembleFile(configuration);				return true;			}			catch {				return false;			}		}		#region Get configuration		[NonAction]		private ResumableConfiguration GetUploadConfiguration(MultipartFormDataStreamProvider provider)		{			return ResumableConfiguration.Create(identifier: GetId(provider), filename: GetFileName(provider), chunks: GetTotalChunks(provider));		}		[NonAction]		private string GetFileName(MultipartFormDataStreamProvider provider)		{			var filename = provider.FormData["resumableFilename"];			return !String.IsNullOrEmpty(filename) ? filename : provider.FileData[0].Headers.ContentDisposition.FileName.Trim(‘\"‘);		}		[NonAction]		private string GetId(MultipartFormDataStreamProvider provider)		{			var id = provider.FormData["resumableIdentifier"];			return !String.IsNullOrEmpty(id) ? id : Guid.NewGuid().ToString();		}		[NonAction]		private int GetTotalChunks(MultipartFormDataStreamProvider provider)		{			var total = provider.FormData["resumableTotalChunks"];			return !String.IsNullOrEmpty(total) ? Convert.ToInt32(total) : 1;		}		[NonAction]		private int GetChunkNumber(MultipartFormDataStreamProvider provider)		{			var chunk = provider.FormData["resumableChunkNumber"];			return !String.IsNullOrEmpty(chunk) ? Convert.ToInt32(chunk) : 1;		}		#endregion		#region Chunk methods		[NonAction]		private string GetChunkFileName(int chunkNumber, string identifier)		{			return Path.Combine(root, string.Format("{0}_{1}", identifier, chunkNumber.ToString()));		}		[NonAction]		private void RenameChunk(MultipartFileData chunk, int chunkNumber, string identifier)		{			string generatedFileName = chunk.LocalFileName;			string chunkFileName = GetChunkFileName(chunkNumber, identifier);			if (File.Exists(chunkFileName)) File.Delete(chunkFileName);			File.Move(generatedFileName, chunkFileName);		}		[NonAction]		private string GetFilePath(ResumableConfiguration configuration)		{			return Path.Combine(root, configuration.Identifier);		}		[NonAction]		private bool ChunkIsHere(int chunkNumber, string identifier)		{			string fileName = GetChunkFileName(chunkNumber, identifier);			return File.Exists(fileName);		}		[NonAction]		private bool AllChunksAreHere(ResumableConfiguration configuration)		{			for (int chunkNumber = 1; chunkNumber <= configuration.Chunks; chunkNumber++)				if (!ChunkIsHere(chunkNumber, configuration.Identifier)) return false;			return true;		}			[NonAction]		private void TryAssembleFile(ResumableConfiguration configuration)		{			if (AllChunksAreHere(configuration))			{				// Create a single file				var path = ConsolidateFile(configuration);				// Rename consolidated with original name of upload				RenameFile(path, Path.Combine(root, configuration.FileName));				// Delete chunk files				DeleteChunks(configuration);			}		}		[NonAction]		private void DeleteChunks(ResumableConfiguration configuration)		{			for (int chunkNumber = 1; chunkNumber <= configuration.Chunks; chunkNumber++)			{				var chunkFileName = GetChunkFileName(chunkNumber, configuration.Identifier);				File.Delete(chunkFileName);			}		}				[NonAction]		private string ConsolidateFile(ResumableConfiguration configuration)		{			var path = GetFilePath(configuration);			using (var destStream = File.Create(path, 15000))			{				for (int chunkNumber = 1; chunkNumber <= configuration.Chunks; chunkNumber++)				{					var chunkFileName = GetChunkFileName(chunkNumber, configuration.Identifier);					using (var sourceStream = File.OpenRead(chunkFileName))					{						sourceStream.CopyTo(destStream);					}				}				destStream.Close();			}			return path;		}		#endregion		[NonAction]		private string RenameFile(string sourceName, string targetName)		{			targetName = Path.GetFileName(targetName); // Strip to filename if directory is specified (avoid cross-directory attack)			string realFileName = Path.Combine(root, targetName);			if (File.Exists(realFileName)) File.Delete(realFileName);			File.Move(sourceName, realFileName);			return targetName;		}	}}

 github上给的demo里边的代码~完全可以拿来用。然而并没有webapp的代码,我照着java的抄,发生了悲剧。不过稍微改改就好。

贴出来我的代码

<div id="frame">    <link href="http://www.mamicode.com/~/Content/resumablestyle.css" rel="stylesheet" />    <script src="http://www.mamicode.com/~/Scripts/jquery-1.10.2.min.js"></script>    <script src="http://www.mamicode.com/~/Scripts/resumable.js"></script>    <br />    <div class="resumable-drop" ondragenter="jQuery(this).addClass(‘resumable-dragover‘);" ondragend="jQuery(this).removeClass(‘resumable-dragover‘);" ondrop="jQuery(this).removeClass(‘resumable-dragover‘);">        将文件拖拽到此处上传 <a class="resumable-browse"><u>点击选择要上传的文件</u></a>    </div>    <div class="resumable-progress">        <table>            <tr>                <td width="100%"><div class="progress-container"><div class="progress-bar"></div></div></td>                <td class="progress-text" nowrap="nowrap"></td>                <td class="progress-pause" nowrap="nowrap">                    <a href="http://www.mamicode.com/#" onclick="r.upload(); return(false);" class="progress-resume-link"><img src="http://www.mamicode.com/~/Img/resume.png" title="Resume upload" /></a>                    <a href="http://www.mamicode.com/#" onclick="r.pause(); return(false);" class="progress-pause-link"><img src="http://www.mamicode.com/~/Img/pause.png" title="Pause upload" /></a>                </td>            </tr>        </table>    </div>    <ul class="resumable-list"></ul>    <script>        var r = new Resumable({            target: ‘@Url.Content("~/api/File/Upload")‘,            chunkSize: 1 * 1024 * 1024,            simultaneousUploads: 3,            //testChunks: false,            throttleProgressCallbacks: 1,            fileType: ["csv"]            //method: "octet"        });        // Resumable.js isn‘t supported, fall back on a different method        if (!r.support) {            $(‘.resumable-error‘).show();        } else {            // Show a place for dropping/selecting files            $(‘.resumable-drop‘).show();            r.assignDrop($(‘.resumable-drop‘)[0]);            r.assignBrowse($(‘.resumable-browse‘)[0]);            // Handle file add event            r.on(‘fileAdded‘, function (file) {                // Show progress pabr                $(‘.resumable-progress, .resumable-list‘).show();                // Show pause, hide resume                $(‘.resumable-progress .progress-resume-link‘).hide();                $(‘.resumable-progress .progress-pause-link‘).show();                // Add the file to the list                $(‘.resumable-list‘).append(‘<li class="resumable-file-‘ + file.uniqueIdentifier + ‘">Uploading <span class="resumable-file-name"></span> <span class="resumable-file-progress"></span>‘);                $(‘.resumable-file-‘ + file.uniqueIdentifier + ‘ .resumable-file-name‘).html(file.fileName);                // Actually start the upload                r.upload();            });            r.on(‘pause‘, function () {                // Show resume, hide pause                $(‘.resumable-progress .progress-resume-link‘).show();                $(‘.resumable-progress .progress-pause-link‘).hide();            });            r.on(‘complete‘, function () {                // Hide pause/resume when the upload has completed                $(‘.resumable-progress .progress-resume-link, .resumable-progress .progress-pause-link‘).hide();            });            r.on(‘fileSuccess‘, function (file, message) {                // Reflect that the file upload has completed                $(‘.resumable-file-‘ + file.uniqueIdentifier + ‘ .resumable-file-progress‘).html(‘(completed)‘);            });            r.on(‘fileError‘, function (file, message) {                // Reflect that the file upload has resulted in error                $(‘.resumable-file-‘ + file.uniqueIdentifier + ‘ .resumable-file-progress‘).html(‘(file could not be uploaded: ‘ + message + ‘)‘);            });            r.on(‘fileProgress‘, function (file) {                // Handle progress for both the file and the overall upload                $(‘.resumable-file-‘ + file.uniqueIdentifier + ‘ .resumable-file-progress‘).html(Math.floor(file.progress() * 100) + ‘%‘);                $(‘.progress-bar‘).css({ width: Math.floor(r.progress() * 100) + ‘%‘ });            });        }    </script></div>

  css样式,图标文件,都是用的demo里的。直接用就好。css中主要的就是:

/* Reset */#frame {margin:0 auto; width:800px; text-align:left;}/* Uploader: Drag & Drop *//*.resumable-error {display:none; font-size:14px; font-style:italic;}.resumable-drop {padding:15px; font-size:13px; text-align:center; color:#666; font-weight:bold;background-color:#eee; border:2px dashed #aaa; border-radius:10px; margin-top:40px; z-index:9999; display:none;}.resumable-dragover {padding:30px; color:#555; background-color:#ddd; border:1px solid #999;}*/.resumable-error {display:none; font-size:14px; font-style:italic;}.resumable-drop { padding:15px;font-size:13px; text-align:center; color:#666; font-weight:bold;background-color:#eee; border:2px dashed #aaa; border-radius:10px;  z-index:9999; display:none;}.resumable-dragover {padding:30px; color:#555; background-color:#ddd; border:1px solid #999;}/* Uploader: Progress bar */.resumable-progress {margin:30px 0 30px 0; width:100%; display:none;}.progress-container {height:7px; background:#9CBD94; position:relative; }.progress-bar {position:absolute; top:0; left:0; bottom:0; background:#45913A; width:0;}.progress-text {font-size:11px; line-height:9px; padding-left:10px;}.progress-pause {padding:0 0 0 7px;}.progress-resume-link {display:none;}.is-paused .progress-resume-link {display:inline;}.is-paused .progress-pause-link {display:none;}.is-complete .progress-pause {display:none;}/* Uploader: List of items being uploaded */.resumable-list {overflow:auto; margin-right:-20px; display:none;}.uploader-item {width:148px; height:90px; background-color:#666; position:relative; border:2px solid black; float:left; margin:0 6px 6px 0;}.uploader-item-thumbnail {width:100%; height:100%; position:absolute; top:0; left:0;}.uploader-item img.uploader-item-thumbnail {opacity:0;}.uploader-item-creating-thumbnail {padding:0 5px; font-size:9px; color:white;}.uploader-item-title {position:absolute; font-size:9px; line-height:11px; padding:3px 50px 3px 5px; bottom:0; left:0; right:0; color:white; background-color:rgba(0,0,0,0.6); min-height:27px;}.uploader-item-status {position:absolute; bottom:3px; right:3px;}/* Uploader: Hover & Active status */.uploader-item:hover, .is-active .uploader-item {border-color:#4a873c; cursor:pointer; }.uploader-item:hover .uploader-item-title, .is-active .uploader-item .uploader-item-title {background-color:rgba(74,135,60,0.8);}/* Uploader: Error status */.is-error .uploader-item:hover, .is-active.is-error .uploader-item {border-color:#900;}.is-error .uploader-item:hover .uploader-item-title, .is-active.is-error .uploader-item .uploader-item-title {background-color:rgba(153,0,0,0.6);}.is-error .uploader-item-creating-thumbnail {display:none;}

  基本就这么多,简单粗暴.....效果还是很好的,能实现大文件分片上传,多文件上传处理,自带进度条提示,省了好多事,说下参数含义:target:后台API,chunkSize:分片大小,fileType:文件类型。

Html5 js上传插件resumable.js 的使用