当前位置: 首页 > 编程日记 > 正文

Web API之基于H5客户端分段上传大文件

http://www.cnblogs.com/OneDirection/articles/7285739.html

查询很多资料没有遇到合适的,对于MultipartFormDataStreamProvider 也并是很适合,总会出现问题。于是放弃,使用了传统的InputStream 分段处理完之后做merge处理。

前台分段规则 命名要规范,传递分段总文件数便于判定分段上传完成做merge处理。merge需要根据分段顺序并在merge成功后删除分段文件。

var FilePartName = file.name + ".part_" + PartCount + "." + TotalParts;

前台JS代码:

复制代码
<script>$(document).ready(function () {$('#btnUpload').click(function () {var files = $('#uploadfile')[0].files;for (var i = 0; i < files.length; i++) {UploadFile(files[i]);}});});function UploadFileChunk(Chunk, FileName) {var fd = new FormData();fd.append('file', Chunk, FileName);fd.append('UserName', 'ming.lu@genewiz.com');fd.append('BusinessLine', '1');fd.append('ServiceItemType', '109');fd.append('Comment', 'This is order comment for GA order');fd.append('UserId', '43F0FEDF-E9AF-4289-B71B-54807BCB8CD9');$.ajax({type: "POST",url: 'http://localhost:50821/api/customer/GA/SaveSangerCameraOrder',contentType: false,processData: false,data: fd,success: function (data) {alert(data);},error: function (data) {alert(data.status + " : " + data.statusText + " : " + data.responseText);}});}function UploadFile(TargetFile) {// create array to store the buffer chunksvar FileChunk = [];// the file object itself that we will work withvar file = TargetFile;// set up other initial varsvar MaxFileSizeMB = 5;var BufferChunkSize = MaxFileSizeMB * (1024 * 1024);var ReadBuffer_Size = 1024;var FileStreamPos = 0;// set the initial chunk lengthvar EndPos = BufferChunkSize;var Size = file.size;// add to the FileChunk array until we get to the end of the filewhile (FileStreamPos < Size) {// "slice" the file from the starting position/offset, to  the required lengthFileChunk.push(file.slice(FileStreamPos, EndPos));FileStreamPos = EndPos; // jump by the amount readEndPos = FileStreamPos + BufferChunkSize; // set next chunk length}// get total number of "files" we will be sendingvar TotalParts = FileChunk.length;var PartCount = 0;// loop through, pulling the first item from the array each time and sending itwhile (chunk = FileChunk.shift()) {PartCount++;// file name conventionvar FilePartName = file.name + ".part_" + PartCount + "." + TotalParts;// send the fileUploadFileChunk(chunk, FilePartName);}}
</script>
复制代码
MaxFileSizeMB参数设置分段大小 ,测试例子为5M
chunk = FileChunk.shift() 在分段上传成功之后删除已上传分段
1
HTML代码:
  <h2>Test Multiple Chunk Upload</h2><p><input type="file" name="uploadfile" id="uploadfile" multiple="multiple" /><br /><a href="#" id="btnUpload" class="btn btn-primary">Upload file</a></p>

后台webapi controller代码:

复制代码
   /// <summary>/// Upload sanger camera order /// </summary>/// <param name="model">user id and user email are required</param>/// <returns></returns>[HttpPost][Route("api/customer/GA/SaveSangerCameraOrder")][MimeMultipart]public HttpResponseMessage SaveSangerCameraOrder(){var files = HttpContext.Current.Request.Files;if (files.Count <= 0){return new HttpResponseMessage(){StatusCode = System.Net.HttpStatusCode.OK,Content = new StringContent(ConstantStringHelper.API_FAILED)};}//receice request form parametersvar userName = HttpContext.Current.Request.Form["UserName"];var businessLine = HttpContext.Current.Request.Form["BusinessLine"];var serviceItemType = HttpContext.Current.Request.Form["ServiceItemType"];var comment = HttpContext.Current.Request.Form["Comment"];var userId = HttpContext.Current.Request.Form["UserId"];if (string.IsNullOrEmpty(userName)) userName = "UnknownUser";string dateTimeTicket = string.Format("{0:yyyy-MM-dd}", System.DateTime.Now);var storagePath = ConfigurationManager.AppSettings["SangerOrderStorageLocation"].ToString();var fileSavePath = storagePath + @"/" + userName + "/" + dateTimeTicket + "/";foreach (string file in files){var FileDataContent = HttpContext.Current.Request.Files[file];if (FileDataContent != null && FileDataContent.ContentLength > 0){// take the input stream, and save it to a temp folder using// the original file.part name postedvar stream = FileDataContent.InputStream;var fileName = Path.GetFileName(FileDataContent.FileName);var UploadPath = HttpContext.Current.Request.MapPath(fileSavePath);Directory.CreateDirectory(UploadPath);string path = Path.Combine(UploadPath, fileName);if (System.IO.File.Exists(path))System.IO.File.Delete(path);using (var fileStream = System.IO.File.Create(path)){stream.CopyTo(fileStream);}// Once the file part is saved, see if we have enough to merge itUtils UT = new Utils();var isMergedSuccess = UT.MergeFile(path);if (isMergedSuccess){//generate txt document for customer commentstring timeTicket = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now);FileStream fs = new FileStream(UploadPath + timeTicket + "OrderComment.txt", FileMode.Create);//get bytebyte[] data = System.Text.Encoding.Default.GetBytes(comment);//writefs.Write(data, 0, data.Length);//clear and clost streamfs.Flush();fs.Close();}}}return new HttpResponseMessage(){StatusCode = System.Net.HttpStatusCode.OK,Content = new StringContent(ConstantStringHelper.API_SUCCESS)};}
复制代码

工具类代码:

记得关闭stream

复制代码
public class Utils{public string FileName { get; set; }public string TempFolder { get; set; }public int MaxFileSizeMB { get; set; }public List<String> FileParts { get; set; }public Utils(){FileParts = new List<string>();}/// <summary>/// original name + ".part_N.X" (N = file part number, X = total files)/// Objective = enumerate files in folder, look for all matching parts of split file. If found, merge and return true./// </summary>/// <param name="FileName"></param>/// <returns></returns>public bool MergeFile(string FileName){bool rslt = false;// parse out the different tokens from the filename according to the conventionstring partToken = ".part_";string baseFileName = FileName.Substring(0, FileName.IndexOf(partToken));string trailingTokens = FileName.Substring(FileName.IndexOf(partToken) + partToken.Length);int FileIndex = 0;int FileCount = 0;int.TryParse(trailingTokens.Substring(0, trailingTokens.IndexOf(".")), out FileIndex);int.TryParse(trailingTokens.Substring(trailingTokens.IndexOf(".") + 1), out FileCount);// get a list of all file parts in the temp folderstring Searchpattern = Path.GetFileName(baseFileName) + partToken + "*";string[] FilesList = Directory.GetFiles(Path.GetDirectoryName(FileName), Searchpattern);//  merge .. improvement would be to confirm individual parts are there / correctly in sequence, a security check would also be important// only proceed if we have received all the file chunksif (FilesList.Count() == FileCount){// use a singleton to stop overlapping processesif (!MergeFileManager.Instance.InUse(baseFileName)){MergeFileManager.Instance.AddFile(baseFileName);if (File.Exists(baseFileName))File.Delete(baseFileName);// add each file located to a list so we can get them into // the correct order for rebuilding the fileList<SortedFile> MergeList = new List<SortedFile>();foreach (string file in FilesList){SortedFile sFile = new SortedFile();sFile.FileName = file;baseFileName = file.Substring(0, file.IndexOf(partToken));trailingTokens = file.Substring(file.IndexOf(partToken) + partToken.Length);int.TryParse(trailingTokens.Substring(0, trailingTokens.IndexOf(".")), out FileIndex);sFile.FileOrder = FileIndex;MergeList.Add(sFile);}// sort by the file-part number to ensure we merge back in the correct ordervar MergeOrder = MergeList.OrderBy(s => s.FileOrder).ToList();using (FileStream FS = new FileStream(baseFileName, FileMode.Create)){try{// merge each file chunk back into one contiguous file streamforeach (var chunk in MergeOrder){using (FileStream fileChunk = new FileStream(chunk.FileName, FileMode.Open)){fileChunk.CopyTo(FS);fileChunk.Flush();fileChunk.Close();fileChunk.Dispose();}}foreach (var item in FilesList){if (File.Exists(item))File.Delete(item);}}catch (Exception ex){FS.Flush();FS.Close();FS.Dispose();throw new Exception(ex.Message);}}rslt = true;// unlock the file from singletonMergeFileManager.Instance.RemoveFile(baseFileName);}}return rslt;}public List<string> SplitFiles(){//bool rslt = false;string BaseFileName = Path.GetFileName(FileName);// set the size of file chunk we are going to split intoint BufferChunkSize = 5 * (1024 * 1024); //5MB// set a buffer size and an array to store the buffer data as we read itconst int READBUFFER_SIZE = 1024;byte[] FSBuffer = new byte[READBUFFER_SIZE];// open the file to read it into chunksusing (FileStream FS = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.Read)){// calculate the number of files that will be createdint TotalFileParts = 0;if (FS.Length < BufferChunkSize){TotalFileParts = 1;}else{float PreciseFileParts = ((float)FS.Length / (float)BufferChunkSize);TotalFileParts = (int)Math.Ceiling(PreciseFileParts);}int FilePartCount = 0;// scan through the file, and each time we get enough data to fill a chunk, write out that filewhile (FS.Position < FS.Length){string FilePartName = String.Format("{0}.part_{1}.{2}",BaseFileName, (FilePartCount + 1).ToString(), TotalFileParts.ToString());FilePartName = Path.Combine(TempFolder, FilePartName);FileParts.Add(FilePartName);using (FileStream FilePart = new FileStream(FilePartName, FileMode.Create)){int bytesRemaining = BufferChunkSize;int bytesRead = 0;while (bytesRemaining > 0 && (bytesRead = FS.Read(FSBuffer, 0,Math.Min(bytesRemaining, READBUFFER_SIZE))) > 0){FilePart.Write(FSBuffer, 0, bytesRead);bytesRemaining -= bytesRead;}}// file written, loop for next chunkFilePartCount++;}}return FileParts;//return rslt;}}public struct SortedFile{public int FileOrder { get; set; }public String FileName { get; set; }}public class MergeFileManager{private static MergeFileManager instance;private List<string> MergeFileList;private MergeFileManager(){try{MergeFileList = new List<string>();}catch { }}public static MergeFileManager Instance{get{if (instance == null)instance = new MergeFileManager();return instance;}}public void AddFile(string BaseFileName){MergeFileList.Add(BaseFileName);}public bool InUse(string BaseFileName){return MergeFileList.Contains(BaseFileName);}public bool RemoveFile(string BaseFileName){return MergeFileList.Remove(BaseFileName);}}
复制代码

如该文件分六段上传,则会提示六次success,可以按照自己业务处理。

转载于:https://www.cnblogs.com/yibinboy/p/7746973.html

相关文章:

对MySQL进行逻辑卷备份与恢复

ZRM 我之前我介绍过&#xff0c;这里就不多少了。以下是关于用mysql-zrm 来测试 基于LVM 逻辑卷管理的数据库全库备份。我这里用的是SUN 的VBOX 虚拟机来做的测试&#xff0c;基于Red Hat AS 5.3。1. 先建立逻辑卷。fdisk 我就不介绍了&#xff0c;这里演示下怎么用创建逻辑卷以…

医保退费主要流程

1.系统初始化Init GetInvoiceInfo with QryInvoice dobeginClose;ParamByName(DanJuID).AsString:edtDjid.Text;Open;vJiuZhenID:FieldByName(JiuZhenID).AsInteger;GetClinicInfo(vJiuZhenID);//获得就诊信息pnlDjrq.Caption:FieldByName(SerialNo).AsString;pnlSkr.Caption:F…

oo第一单元总结

第一次作业 第一次作业自己虽然很想向着面向对象的方向上写&#xff0c;但写出来还是很C语言式的程序。从头到尾扫描字符串&#xff0c;扫到加减号便认为接下来是一项&#xff0c;再用正则表达式去分情况匹配出这一项。用Hashmap来存储数据&#xff0c;方便合并同类项。最后套一…

npm run build打包失败

使用npm run build命令打包Angular项目时报错&#xff0c;错误信息如下&#xff1a; WARNING in budgets, maximum exceeded for initial. Budget 2 MB was exceeded by 3.33 MB.ERROR in budgets, maximum exceeded for initial. Budget 5 MB was exceeded by 340 kB. npm ER…

YII2 models非常好用的控制输出数据【重写Fields】

models里重写Fields真的很好用&#xff0c;用于分类、评论功能 列子&#xff1a;评论表models/Comment.php 1、关联商品表 2、获取父级&#xff08;即管理员&#xff09;评论 public function Fields()//添加parentComment自定义字段输出 { $fields parent::Fields(); $fi…

Visual studio 2005如何实现源码管理

转自CSDN Visual studio 2005如何实现源码管理(Visual Studio .Net团队开发)目录&#xff1a; 〇、 摘要一、 开发前的准备 二、 创建空的SourceSafe数据库 三、 新建项目并加入版本控制 四、 获取SourceSafe中的项目 五、 版本控制的几个概念 六、 版本控制项目的管理 七、 总…

error while loading shared libraries: libstdc++.so.5: wrong ELF class: ELFCLASS64

今天部署一个探针在运行的时候报了这样一个错&#xff1a;error while loading shared libraries: libstdc.so.5: wrong ELF class: ELFCLASS64 [rootdb152 rma_p_unix]# ldd xxxxlinux-gate.so.1 > (0x00dd7000)libstdc.so.5 > not found # 发现这边动态库找不着 这…

package.json 依赖包版本号

依赖包版本号格式&#xff1a;major.minor.patch major 为主版本号(大版本号)&#xff0c;变化了表示有了一个不兼容上个版本的大更改。 minor 为次版本号(小版本号)&#xff0c;变化了表示增加了新功能&#xff0c;并且可以向后兼容。 patch 为修订版本号&#xff0c;变化了…

.net下绘制统计图工具-请推荐

需要利用到行情、数据频道需要多种样式的表现形式&#xff0c;包括 饼图、柱图、折线图等等 重点是&#xff1a;展示效果好&#xff0c;开发效率高 以前用过dundas chart&#xff0c;不知道有没能生产flash的。 小弟初来乍到&#xff0c;还请给位不吝赐教 放两天置顶&#xff…

wireless(二维数组前缀和)

1 &#xff0e; 无线网络发射器选址(wireless.cpp/c/pas)【问题描述】随着智能手机的日益普及&#xff0c;人们对无线网的需求日益增大。某城市决定对城市内的公共场所覆盖无线网。假设该城市的布局为由严格平行的129条东西向街道和129条南北向街道所形成的网格状&#xff0c;并…

SQL Server 2000 从哪里看是哪个版本

有两种方法&#xff1a; 第一步&#xff1a;使用SQL语句查询 select version 查询结果如下&#xff1a; Microsoft SQL Server 2000 - 8.00.2039 (Intel X86) May 3 2005 23:18:38 Copyright (c) 1988-2003 Microsoft Corporation Personal Edition on Windows NT 5.1 (Build 2…

【洛谷p1313】计算系数

&#xff08;%%%hmr&#xff09; 计算系数【传送门】 算法呀那个标签&#xff1a; &#xff08;越来越懒得写辽&#xff09;&#xff08;所以今天打算好好写一写&#xff09; 首先&#xff08;axby&#xff09;k的计算需要用到二项式定理&#xff1a; 对于&#xff08;xy&#…

CMD——ping及用其检测网络故障

Ping命令全称Packet Internet Grope&#xff0c;即因特网包探测器。通过调用ICMP&#xff08;因特网控制报文协议&#xff09;&#xff0c;发送一份ICMP回显请求给目的主机&#xff0c;并等待返回ICMP回显应答。一般用来测试源主机到目的主机网络的连通性&#xff08;只有在安装…

TSLint 规则

除了在全局配置中使用TSLint规则&#xff0c;还可以在文件中使用TSLint规则。 当不想修改全局配置中的TSLint规则时&#xff0c;可以在文件中使用以下注释规则标志对TSLint规则进行修改。 // tslint:disable —— 忽略该行以下所有代码出现的错误提示&#xff0c;可以在文件首…

Weblogic禁用SSLv3和RC4算法教程

weblogic在启用https时一样会报同WebSphere那样的一SSL类漏洞&#xff0c;中间件修复这些漏洞原理上来说是一样的&#xff0c;只是在具体操作上有着较大的区别。 1. weblogic禁用SSLv3算法 编缉$DOMAIN_HOME/bin目录下的setDomainEnv.sh&#xff0c;找到"JAVA_OPTIONS&quo…

转《两个个很形象的依赖注入的比喻》

何谓控制反转&#xff08;IoC Inversion of Control&#xff09;&#xff0c;何谓依赖注入&#xff08;DI Dependency Injection&#xff09;&#xff1f;一直都半懂不懂&#xff0c;今天看到两个比喻&#xff0c;觉得比较形象。 IoC&#xff0c;用白话来讲&#xff0c;就是…

线程上下文设计模式

import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream;public class Test {public static void main(String[] args){ThreadLocalExample.test();} }/*21.2 线程上下文设计*/class ApplicationConfigurat…

自定义控件--基础2

Control类程序按控件的步骤: 呈现控件的步骤 1.RenderControl方法 代码如下: protected void RenderControl(HtmlTextWriter writer) { if(Visible) { Render(writer);}} 先判断Visible,然后进行Render.2.Render方法 public virtual void Render(HtmlTextWriter writer) { Rend…

input输入框为number类型时,去掉上下小箭头

input输入框type为number时&#xff0c;去掉上下小箭头&#xff0c;方式如下&#xff1a; <input type"number" ...><style>/* 在Chrome浏览器下 */input::-webkit-outer-spin-button,input::-webkit-inner-spin-button {-webkit-appearance: none;}/* 在…

数据库和数据仓库的区别

简而言之&#xff0c;数据库是面向事务的设计&#xff0c;数据仓库是面向主题设计的。 数据库一般存储在线交易数据&#xff0c;数据仓库存储的一般是历史数据。 数据库设计是尽量避免冗余&#xff0c;一般采用符合范式的规则来设计&#xff0c;数据仓库在设计是有意引入冗余&a…

我是主考官:两次弃用的变态笔试题

故事&#xff08;3&#xff09;&#xff1a;两次弃用的变态笔试题电话的沟通虽然不可能对一个程序员作全面的了解&#xff0c;但基本上能有一个比较概括的判断&#xff0c;这也许就是所谓的第一印象吧&#xff01;通过电话的初步沟通我对来面试的程序员已经有了初步的印象&…

[Swift]LeetCode901. 股票价格跨度 | Online Stock Span

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号&#xff1a;山青咏芝&#xff08;shanqingyongzhi&#xff09;➤博客园地址&#xff1a;山青咏芝&#xff08;https://www.cnblogs.com/strengthen/&#xff09;➤GitHub地址&a…

java基础===点餐系统

public class OrderMsg {public static void main(String[] args) throws Exception { /** * 订餐人姓名、选择菜品、送餐时间、送餐地址、订单状态、总金额 * 01.创建对应的数组 * 02.数组的初始化 * 03.显示菜单 * 04.根据用户的选择进去指定的模块 */ String[] names new S…

HTML页面中使两个div并排显示

在HTML中实现两个div并排显示&#xff0c;方法如下&#xff1a; 方法1&#xff1a;设置float浮动对需要并排显示的div设置样式&#xff1a;style"float:left;" <div style"float:left;">div1</div>方法2&#xff1a;设置div为行内样式对需要并…

备案网站管理系统是JSP做的

备案网站管理系统 http://www.miibeian.gov.cn/ 浪费了我一上午的时间没成功.靠!转载于:https://www.cnblogs.com/splyn/archive/2009/12/24/1631281.html

explorer.exe应用程序错误说明 0X000000该内存不能为read的解决方法

0X000000该内存不能为read的解决方法 出现这个现象有方面的&#xff0c;一是硬件&#xff0c;即内存方面有问题&#xff0c;二是软件&#xff0c;这就有多方面的问题了。 一&#xff1a;先说说硬件&#xff1a; 一般来说&#xff0c;电脑硬件是很不容易坏的。内存出现问题的可能…

CSS 选择符

选择符 selector 样式的基本规则——样式声明与关键字 声明块中有一个或多个声明。声明的格式是固定的&#xff0c;先是属性名&#xff0c;然后是冒号&#xff0c;后面再跟属性值和分号。冒号和分号后面可以有零个或多个空白。属性值几乎都是一个关键字或以空格分隔的多个关键…

CSS3快学笔记

在编写CSS3样式时&#xff0c;不同的浏览器可能需要不同的前缀。它表示该CSS属性或规则尚未成为W3C标准的一部分&#xff0c;是浏览器的私有属性&#xff0c;虽然目前较新版本的浏览器都是不需要前缀的&#xff0c;但为了更好的向前兼容前缀还是少不了的。 前缀 浏览器 -webk…

DOS批处理的字符串功能

DOS批处理的字符串功能 批处理有着具有非常强大的字符串处理能力&#xff0c;其功能绝不低于C语言里面的字符串函数集。批处理中可实现的字符串处理功能有&#xff1a;截取字符串内容、替换字符串特定字段、合并字符串、扩充字符串等功能。下面对这些功能一一进行讲解。  【 …

走进Java 7模块系统

笔者在观看过Devoxx关于Jigsaw的一段演示后&#xff0c;我很兴奋&#xff0c;觉得它应该会是针对复杂类路径版本问题和JAR陷阱等问题的解决方案。开发者最终能够使用他们所期望的任何Xalan版本&#xff0c;而无需被迫使用授权机制。不幸的是&#xff0c;通往更加有效的模块系统…