using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Configuration; namespace WebApplication1
{
/// <summary>
/// PartialFileSet 的摘要说明
/// </summary>
public class UpLoadPartialFile : IHttpHandler
{
public static DateTime mkdirRecodeTime = DateTime.Now.AddDays(-);
public static string keyHead = DateTime.Now.Year + "" + DateTime.Now.Month + DateTime.Now.Day;
public static string path = ConfigurationManager.AppSettings["filePath"].ToString();
public static object mkdirLock = new object(); /// <summary>
/// 根据日期字符串返回路径
/// </summary>
/// <param name="keyHead">日期字符串yyyyMMdd</param>
/// <returns></returns>
private static string getCurrentPathByDate(string keyHead)
{ return path + "\\" + keyHead;
} private static string getCurrentFilePathByDate(string keyHead, string guid, int id)
{
return getCurrentPathByDate(keyHead) + "\\" + guid + id;
} private static string getNewFilePathByDate(string keyHead, string guid)
{
string fileType = HttpContext.Current.Request["fileType"];
if(fileType!=null&& fileType.Length>)
return getCurrentPathByDate(keyHead) + "\\" + guid+"."+fileType;
return getCurrentPathByDate(keyHead) + "\\" + guid;
}
/// <summary>
/// 如果目标文件夹不存在 创建文件夹
/// </summary>
/// <param name="keyHead"></param>
private static void mkdirIfNoExit()
{ if ((DateTime.Now-mkdirRecodeTime ).Days < )
return; //创建文件夹目录
if (Directory.Exists(getCurrentPathByDate(keyHead)))
return;
lock (mkdirLock)
{ if (!Directory.Exists(path))
Directory.CreateDirectory(path);
if (!Directory.Exists(getCurrentPathByDate(keyHead)))
Directory.CreateDirectory(getCurrentPathByDate(keyHead)); keyHead = DateTime.Now.Year + "" + DateTime.Now.Month + DateTime.Now.Day;
mkdirRecodeTime = DateTime.Now;
return;
} } private void uploadFile(HttpContext context)
{
try
{
//检查目录
mkdirIfNoExit(); if (context.Request.Files == null || context.Request.Files.Count == || context.Request.Files[].InputStream.Length == )
{
context.Response.Write("{\"state\":\"error\",\"code\":-6,\"msg\":\"接口调用出错 上传文件不能为空\"}");
return;
}
if (context.Request.Files.Count > )
{
context.Response.Write("{\"state\":\"error\",\"code\":-7,\"msg\":\"接口调用出错 每次只能上传单个文件\"}");
return;
}
string guid = Guid.NewGuid().ToString(); string keyHead = DateTime.Now.Year + "" + DateTime.Now.Month + DateTime.Now.Day;
string currentPath = getCurrentPathByDate(keyHead);
string filePath = getNewFilePathByDate(keyHead, guid); lock (filePath)
{
//创建文件
try
{
context.Request.Files[].SaveAs(filePath); }
catch (Exception e)
{
Util.LogHelper.Info(e.Message + e.StackTrace); context.Response.Write("{\"state\":\"error\",\"code\":-2,\"msg\":\"接口调用出错 guid为" + guid + "的文件写入时出现错误 已记录日志\"}");
return;
}
}
context.Response.Write("{\"state\":\"success\",\"code\":0,\"msg\":\""+keyHead+guid+"\"}"); }
catch (Exception e)
{
Util.LogHelper.Info(e.Message + e.StackTrace);
context.Response.Write("{\"state\":\"error\",\"code\":-1,\"msg\":\"接口调用出错 已记录日志\"}");
}
} private void uploadPartialFile(HttpContext context) {
try
{
//检查目录
mkdirIfNoExit();
if (context.Request["date"] == null)
{
context.Response.Write("{\"state\":\"error\",\"code\":-2,\"msg\":\"接口调用出错 参数date(第一片上传时间)不能为空\"}");
return;
}
DateTime uploadData;
if (!DateTime.TryParse(context.Request["date"], out uploadData))
{ context.Response.Write("{\"state\":\"error\",\"code\":-2,\"msg\":\"接口调用出错date参数错误 格式 yyyy-MM-dd\"}");
return;
}
if (context.Request["guid"] == null || context.Request["guid"].Trim().Length != )
{
context.Response.Write("{\"state\":\"error\",\"code\":-3,\"msg\":\"接口调用出错 guid(文件唯一标示)不能为空且必须为32位长度\"}");
return;
} if (context.Request["id"] == null || context.Request["id"].Trim().Length == )
{
context.Response.Write("{\"state\":\"error\",\"code\":-4,\"msg\":\"接口调用出错 id(文件分组id)不能为空\"}");
return;
}
int id = -;
if (!int.TryParse(context.Request["id"], out id))
{
context.Response.Write("{\"state\":\"error\",\"code\":-5,\"msg\":\"接口调用出错 id(文件分组id)必须为数字\"}");
return;
} if (context.Request.Files == null || context.Request.Files.Count == || context.Request.Files[].InputStream.Length == )
{
context.Response.Write("{\"state\":\"error\",\"code\":-6,\"msg\":\"接口调用出错 上传文件不能为空\"}");
return;
}
if (context.Request.Files.Count > )
{
context.Response.Write("{\"state\":\"error\",\"code\":-7,\"msg\":\"接口调用出错 每次只能上传单个文件\"}");
return;
}
string guid = context.Request["guid"].Trim();
string keyHead = uploadData.Year + "" + uploadData.Month + uploadData.Day;
string currentPath = getCurrentPathByDate(keyHead);
string filePath = getCurrentFilePathByDate(keyHead, guid, id);
if (File.Exists(filePath))
{
context.Response.Write("{\"state\":\"error\",\"code\":-8,\"msg\":\"接口调用出错 guid为" + guid + "文件id为" + id + "的文件已存在\"}");
return;
}
lock (filePath)
{
if (File.Exists(filePath))
{
context.Response.Write("{\"state\":\"error\",\"code\":-8,\"msg\":\" guid为" + guid + "文件id为" + id + "的文件已存在\"}");
return;
}
//创建文件
try
{
context.Request.Files[].SaveAs(filePath); }
catch (Exception e)
{
Util.LogHelper.Info(e.Message + e.StackTrace); context.Response.Write("{\"state\":\"error\",\"code\":-9,\"msg\":\"接口调用出错 guid为" + guid + "文件id为" + id + "的文件写入时出现错误 已记录日志\"}");
return;
}
} context.Response.Write("{\"state\":\"success\",\"code\":0,\"msg\":\" guid为" + guid + "文件id为" + id + "的文件写入成功\"}"); }
catch (Exception e)
{
Util.LogHelper.Info(e.Message + e.StackTrace);
context.Response.Write("{\"state\":\"error\",\"code\":-1,\"msg\":\"接口调用出错 已记录日志\"}");
}
} private void joinfile(HttpContext context)
{
try
{
//检查目录
if (context.Request["date"] == null)
{
context.Response.Write("{\"state\":\"error\",\"code\":-2,\"msg\":\"接口调用出错 参数date(第一片上传时间)不能为空\"}");
return;
}
if (context.Request["guid"] == null || context.Request["guid"].Trim().Length != )
{
context.Response.Write("{\"state\":\"error\",\"code\":-3,\"msg\":\"接口调用出错 guid(文件唯一标示)不能为空且必须为32位长度\"}");
return;
} if (context.Request["idArray"] == null || context.Request["idArray"].Trim().Length == )
{
context.Response.Write("{\"state\":\"error\",\"code\":-4,\"msg\":\"接口调用出错 id(文件分组id列表)不能为空\"}");
return;
}
DateTime uploadData;
if (!DateTime.TryParse(context.Request["date"],out uploadData)) { context.Response.Write("{\"state\":\"error\",\"code\":-6,\"msg\":\"接口调用出错date参数错误 格式 yyyy-MM-dd\"}");
return;
} string keyHead = uploadData.Year + "" + uploadData.Month + uploadData.Day;
string guid = context.Request["guid"].Trim(); List<int> IdArray = context.Request["idArray"].StringArrayConvertInt(',').OrderBy(u=>u).ToList();
//开始检查文件是否全部存在
List<string> pathList = new List<string>();
if (IdArray.Count < )
{
context.Response.Write("{\"state\":\"error\",\"code\":-8,\"msg\":\"接口调用出错文件列表文件少于2个无法进行合并操作 请检查IdArray参数\"}");
return;
}
foreach (var item in IdArray)
{
string path = getCurrentFilePathByDate(keyHead,guid,item);
if (!File.Exists(path))
{
context.Response.Write("{\"state\":\"error\",\"code\":-7,\"msg\":\"id编号为"+item+"的文件在服务器上不存在 请检查\"}");
return;
}
pathList.Add(path); }
string newGuid = Guid.NewGuid().ToString();
string newFilePath = getNewFilePathByDate(keyHead,newGuid);
using (System.IO.FileStream fileStram = File.Open(newFilePath, FileMode.Create,FileAccess.Write))
{ //开始合并文件 创建一个副本
pathList.ForEach(u =>
{
//读取文件
using (FileStream save = new FileStream(u, FileMode.Open, FileAccess.Read))
{
byte[] bt = new byte[];
int count = -;
while ((count = save.Read(bt, , bt.Length)) > )
{
fileStram.Write(bt, , count);
}
} });
context.Response.Write("{\"state\":\"success\",\"code\":0,\"msg\":\""+ keyHead + newGuid + "\"}");
//删除文件列表
delFile(pathList);
} }
catch (Exception e)
{
Util.LogHelper.Info(e.Message + e.StackTrace);
context.Response.Write("{\"state\":\"error\",\"code\":-1,\"msg\":\"接口调用出错 已记录日志\"}");
}
}
public bool IsReusable
{
get
{
return false;
}
}
public static void delFile(List<string> pathList) {
try
{ for (int i = ; i < pathList.Count; i++)
{
File.Delete(pathList[i].Replace("\\","/"));
}
}
catch (Exception e)
{
Util.LogHelper.Info("删除分片文件错误"+e.Message+e.StackTrace);
}
} /// <summary>
/// 创建部分文件集合
/// </summary>
/// <param name="context"></param>
public void ProcessRequest(HttpContext context)
{
try
{ context.Response.ContentType = "application/json"; string mode = context.Request["mode"].ToLower();
if (mode == "partialfile")
uploadPartialFile(context);
else if (mode == "joinfile")
joinfile(context);
else if (mode == "uploadfile")
uploadFile(context);
else
context.Response.Write("{\"state\":\"error\",\"code\":-10,\"msg\":\"接口调用出错 mode值范围为partialfile(上传分片文件)joinfile(合并分片文件)uploadfile上传单个文件 三种 \"}"); } catch (Exception e)
{ Util.LogHelper.Info("ProcessRequest执行错误" + e.Message + e.StackTrace);
context.Response.Write("{\"state\":\"error\",\"code\":-1,\"msg\":\"接口调用出错 已记录日志\"}"); }
} }
}

js调用

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.js"></script>
<script src="https://cdn.bootcss.com/jquery.form/3.51/jquery.form.js"></script>
<form id="fileUpLoad" action="/UpLoadPartialFile.ashx?mode=partialfile" method="post" name="test">
<input type="file" name="name" onchange="upload(this)" />上传分片文件
<input type="hidden" id="guid" name="guid"value="aaaaaaaaaaaaaaaa" />
<input type="hidden" name="date" value="2017-10-02" />
<input type="hidden" id="id" name="id" value="0" /> </form> <form id="fileUpLoadFile" action="/UpLoadPartialFile.ashx?mode=uploadfile" method="post" name="test">
<input type="file" name="name2" onchange="uploadFile(this)" />上传单个文件 </form>
<input type="text" id="fileType" name="fileType" placeholder="合并的文件后缀名称 可选(合并文件时有效)" value="mp4" />
<button onclick="joinFIle()">合并文件</button> </body>
</html> <script> function newGuid() {
var guid = "";
for (var i = 1; i <= 32; i++) {
var n = Math.floor(Math.random() * 16.0).toString(16);
guid += n; }
return guid;
}
var idArray = '';
var date = 2017-10-02;
var i = 0;
var guid = newGuid();
$('#guid').val(guid);
function joinFIle() { $.ajax('/UpLoadPartialFile.ashx?mode=joinfile&date=2017-10-02&guid=' + guid + '&idArray=' + idArray + '&fileType=' + $('#fileType').val()).done(function (rs) {
alert(rs.msg);
}) }
function upload(obj) { if (obj.size == 0)
return;
//上传表单
$('#id').val(++i);
idArray += i+',';
$('#fileUpLoad').ajaxSubmit(); } function uploadFile() {
$('#fileUpLoadFile').ajaxSubmit();
}
</script>

C#调用

        private static void post1()
{
string url = @"http://localhost:1128/uploadPartialFile.ashx?mode=partialfile&date=2017-10-03&id=1&guid=5ed1eddf7e7213ed0db6d5150b488335";//这里就不暴露我们的地址啦
string modelId = "4f1e2e3d-6231-4b13-96a4-835e5af10394";
string updateTime = "2016-11-03 14:17:25";
string encrypt = "f933797503d6e2c36762428a280e0559"; string filePath = @"D:/test2";
string fileName = "test2";
byte[] fileContentByte = new byte[1024]; // 文件内容二进制 #region 将文件转成二进制 FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
fileContentByte = new byte[fs.Length]; // 二进制文件
fs.Read(fileContentByte, 0, Convert.ToInt32(fs.Length));
fs.Close(); #endregion #region 定义请求体中的内容 并转成二进制 string boundary = "ceshi";
string Enter = "\r\n"; string modelIdStr = "--" + boundary + Enter
+ "Content-Disposition: form-data; name=\"modelId\"" + Enter + Enter
+ modelId + Enter; string fileContentStr = "--" + boundary + Enter
+ "Content-Type:application/octet-stream" + Enter
+ "Content-Disposition: form-data; name=\"fileContent\"; filename=\"" + fileName + "\"" + Enter + Enter; string updateTimeStr = Enter + "--" + boundary + Enter
+ "Content-Disposition: form-data; name=\"updateTime\"" + Enter + Enter
+ updateTime; string encryptStr = Enter + "--" + boundary + Enter
+ "Content-Disposition: form-data; name=\"encrypt\"" + Enter + Enter
+ encrypt + Enter + "--" + boundary + "--"; var modelIdStrByte = Encoding.UTF8.GetBytes(modelIdStr);//modelId所有字符串二进制 var fileContentStrByte = Encoding.UTF8.GetBytes(fileContentStr);//fileContent一些名称等信息的二进制(不包含文件本身) var updateTimeStrByte = Encoding.UTF8.GetBytes(updateTimeStr);//updateTime所有字符串二进制 var encryptStrByte = Encoding.UTF8.GetBytes(encryptStr);//encrypt所有字符串二进制 #endregion HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "multipart/form-data;boundary=" + boundary; Stream myRequestStream = request.GetRequestStream();//定义请求流 #region 将各个二进制 安顺序写入请求流 modelIdStr -> (fileContentStr + fileContent) -> uodateTimeStr -> encryptStr myRequestStream.Write(modelIdStrByte, 0, modelIdStrByte.Length); myRequestStream.Write(fileContentStrByte, 0, fileContentStrByte.Length);
myRequestStream.Write(fileContentByte, 0, fileContentByte.Length); myRequestStream.Write(updateTimeStrByte, 0, updateTimeStrByte.Length); myRequestStream.Write(encryptStrByte, 0, encryptStrByte.Length); #endregion HttpWebResponse response = (HttpWebResponse)request.GetResponse();//发送 Stream myResponseStream = response.GetResponseStream();//获取返回值
StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8")); string retString = myStreamReader.ReadToEnd(); myStreamReader.Close();
myResponseStream.Close();
}

Code=0表示接口调用成功

其他情况下code为负数 msg中包含具体错误信息

单个分片文件上传不能超过4000kb

上传分片文件

http://localhost:1128/UpLoadPartialFile.ashx?mode=partialfile

method:post

参数

date 分片上传 参数格式yyyy-MM-dd日期 例如:     2017-10-02 所有分片上传都使用一个时间 服务端根据这个参数来区分文件夹

id  (整数) 分片文件编号 如1  服务端合并文件将根据编号由小到大排序合并

guid 客户端生成一个guid 后面所有同一个文件的分片均使用同一个guid

guid为32位长度 即省略-分隔符

浏览器js调用报文如下

调用成功返回json

{"state":"success","code":0,"msg":" guid为db334a03fc01b200c770871374734c7b文件id为1的文件写入成功"}

合并分片文件

http://localhost:1128/UpLoadPartialFile.ashx?mode=joinfile&date=2017-10-02&guid=db334a03fc01b200c770871374734c7b&idArray=1,2,&fileType=mp4

method :post

参数

date 分片上传时间(必选)

guid 上传分片时使用的guid (必选)

idArray 编号列表 拼接逗号分隔 需要两个以上 否则无法合并文件(必选)

filetype 文件后缀名称 可选 如果传递该参数 读取时也要传递 否则无法找到文件

调用成功返回

{"state":"success","code":0,"msg":"2017102cdb64b77-5f7c-4ccf-aa11-1e327dfcd49f"}

Msg中的字符串用于读取文件

上传单个文件

http://localhost:1128/UpLoadPartialFile.ashx?mode=uploadfile

method:post

文件流写入到请求中

调用成功返回

{"state":"success","code":0,"msg":"2017102cdb64b77-5f7c-4ccf-aa11-1e327dfcd49f"}

Msg中的字符串用于读取文件

安卓参考实现

http://blog.csdn.net/ylbf_dev/article/details/50468984

ios参考实现

http://www.jianshu.com/p/a0e3c77d3164

C#实现分片上传文件的更多相关文章

  1. django实现分片上传文件

    目标:利用django实现上传文件功能 1,先设置路由系统 urls.py from django.conf.urls import url,include from django.contrib i ...

  2. node.js分片上传文件

    前端 : <html> <head> <title>分片上传文件</title> </head> <body> <div ...

  3. WebUploader分片断点上传文件(二)

    写在前面: 这几天,有去研究一下WebUploader上传文件,前面的博客有记录下使用WebUploader简单上传文件的例子,今天就把分片断点上传的例子也记录下吧,在博客园中,也查看了一些资料,基本 ...

  4. Python之requests模块-大文件分片上传

    最近在做接口测试时,拿到一个分片上传文件的接口,http接口请求头中的Content-Type为multipart/form-data.需要在客户端将大文件分片成数据块后,依次传给服务端,由服务端还原 ...

  5. java下载网络大文件之内存不够的解决办法(包含分片上传分片下载)

    一.背景 2020年11月份的时候,我做过一个项目,涉及到网络文件,比如第三方接口提供一个文件的下载地址,使用java去下载,当时我全部加在到JVM内存里面,话说,单单是80M的下载单线程没问题,但是 ...

  6. java后端分片上传接口

    文件上传工具--FileUtil package com.youmejava.chun.util; import lombok.Data; import org.apache.tomcat.util. ...

  7. 用百度webuploader分片上传大文件

    一般在做文件上传的时候,都是通过客户端把要上传的文件上传到服务器,此时上传的文件都在服务器内存,如果上传的是视频等大文件,那么服务器内存就很紧张,而且一般我们都是用flash或者html5做异步上传, ...

  8. 打造 html5 文件上传组件,实现进度显示及拖拽上传,支持秒传+分片上传+断点续传,兼容IE6+及其它标准浏览器

    老早就注册了博客园帐号,昨天才发现,连博客都没开,Github也是一样,深觉惭愧,赶紧潜个水压压惊`(*∩_∩*)′ 言归正传.大概许多人都会用到文件上传的功能,上传的库貌似也不少,比如(jQuery ...

  9. 利用HTML5分片上传超大文件

    在网页中直接上传大文件一直是个比较头疼的问题,主要面临的问题一般包括两类:一是上传时间长中途一旦出错会导致前功尽弃:二是服务端配置复杂,要考虑接收超大表单和超时问题,如果是托管主机没准还改不了配置,默 ...

随机推荐

  1. Ubuntu 16.04安装Wine版的微信(deepin-wechat)

    说明: 1.使用的Wine版本是深度出品(Deepin),已经精简了很多没用的配置,使启动能非常快,占用资源小. 2.关于没有.wine文件夹的解决方法:在命令行上运行winecfg: 3.有可能在今 ...

  2. Django 中文显示

    Django中文显示: 1.如要在.py文件中显示中文,在文件首行加上:# -*- coding: utf-8 -*- 2.如要在html文件中显示中文,要将文件保存为UTF-8格式 3.在setti ...

  3. Android Studio Beat版公布!

    Android Studio Beat版公布了! 速度比0.61快爆了,有木有! L也能更新了,炫爆了,有木有!

  4. 在w7下的wamp中配置memcache

    php版本是5.4.16 ,我的电脑是w7 64位的. 一. memcache和memcached的区别  在自己的新程序中打算全面应用memcached技术,这个很容易理解这是memcached是内 ...

  5. iOS8 NotificationCenter Extension 简介

    在最新的WWDC14上面,苹果发布了iOS8的一些新特性,而其中最让程序员兴奋的特性莫过于Extension,或者称之为Widget. 下面就来尝鲜试验一把. 一.Extension简介 首先,苹果只 ...

  6. zookeeper疑难杂症

    1.zookeeper是怎么写数据的?因为是master写再同步广播到follow节点,如果master写完,following在写的过程中出现失败怎么办? :zookeeper支持原子的写入操作,要 ...

  7. 4 cocos2dx 3.0 源码分析- scheduler

    scheduler 这个类, 负责了引擎的自定义更新, 及定时更新相关的操作, 看看下面的代码,很熟悉吧.   schedule(schedule_selector(HelloWorld::updat ...

  8. sqlmap使用帮助文档(1)

    当给sqlmap这么一个url的时候,它会: 1.判断可注入的参数 2.判断可以用那种SQL注入技术来注入 3.识别出哪种数据库 4.根据用户选择,读取哪些数据 sqlmap支持五种不同的注入模式: ...

  9. Glide Picasso Fresco UIL 图片框架 缓存 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  10. 更改DNS轻松访问google.com,FaceBook,Youtube等

    将默认的Dns更改为42.120.21.30即可打开 https://www.google.com/ https://www.facebook.com/ https://www.youtube.com ...