那么多单纯算单纯?多蛮横算蛮横?我说你能不写一句标识符,你信吗?间接把两个文档往IIS服务项目器上一扔,就全力支持浏览(IIS服务项目端预设全力支持)在贴标识符之前先来了解下什么是ease(这儿说的是浏览ease)?呢同时实现的ease?
ease是浏览了三分之一断网或者中止了,接着能接着浏览不必Cubzac浏览很奇妙吗,其实单纯得很,他们仅此罢了能想到的首先顾客端向服务项目端推送两个允诺(浏览文档)接着服务项目端积极响应允诺,信息包涵文档总大小不一、文档流开始和结束边线、内容大小不一等。
那具体是呢同时实现的呢?HTTP/1.1有位头特性Range比如说你推送允诺的这时候所带Range:0-199,等同于你是允诺0到199间的统计数据接着服务项目器积极响应允诺Content-Range: bytes 0-199/250 ,表示你以获取了0到199间的统计数据,总大小不一是250。
(也是说你还有统计数据没浏览完)他们来画个图吧。
呢很单纯?那么奇妙的小东西也是个“签订合同”罢了,也是简而言之的HTTP协定然而,协定这小东西你严格遵守它就存有,不严格遵守它就不存有就像传奇服务端初年的钱我们都信它,它就管用如果大部分人不信它,也就没卵用了这个ease也是这样。
你服务项目端严格遵守就全力支持,不严格遵守也就不全力支持ease所以他们写浏览辅助工具的这时候需要推论积极响应数据流里呢Content-Range,来确定是否全力支持ease文档浏览-服务项目端采用a条码提供更多文档浏览利用a条码来浏览文档,也是他们后面说的不写标识符就能同时实现浏览。
间接把文档往iis服务项目器上一扔,接着把镜像贴到a条码上,谁知浏览单纯、蛮横别说了如迪塞县那么好那我们也不会费心去写其他浏览方传奇服务端了这儿有位毁灭性的优点这种方式提供更多的浏览不如安全可靠。
谁都能浏览,没职权控制,没准还会被人文档扫描器(好像csdn就上过这阵子事)采用Response.TransmitFile提供更多文档浏览下面说间接a条码提供更多浏览不如安全可靠那他们呢提供更多相对安全可靠的浏览呢ASP.NET 预设App_Data文档夹是不能被间接出访的,那他们把浏览文档放这儿面。
接着浏览的这时候他们加载文档在回到到积极响应流//文档浏览public void FileDownload5(){ //后面能做采用者登入校正、采用者职权校正等 string filename = "大统计数据.rar"; //顾客端留存的文档名 。
string filePath = Server.MapPath("/App_Data/大统计数据.rar");//要被浏览的文档方向 Response.ContentType = "application/octet-stream"; //二进制流
Response.AddHeader("Content-Disposition", "attachment;filename=" + filename); Response.TransmitFile(filePath); //将指定文档写入 HTTP 积极响应输出流
}其他方式文档浏览在网上搜索C#文档浏览一般都会搜到简而言之的“四种方式”其实那些标识符并不能拿来间接采用,有坑的第一种:(Response.BinaryWrite) public void FileDownload2()。
{ string fileName = "增建文档夹2.rar";//顾客端留存的文档名 string filePath = Server.MapPath("/App_Data/增建文档夹2.rar");//要被浏览的文档方向
Response.ContentType = "application/octet-stream";//二进制流 //通知浏览器浏览文档而不是打开 Response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8));
//以字符流的形式浏览文档 using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{ Response.AddHeader("Content-Length", fs.Length.ToString()); //这儿容易内存溢出 //理论上数组最大长度 int.MaxValue 2147483647
//(实际分不到那么多,不同的程序能分到值也不同,本人机器,winfrom( 2147483591 相差56)、iis(也差不多2G)、iis Express(只有100多MB)) byte[] bytes = new byte[(int)fs.Length];
fs.Read(bytes, 0, bytes.Length); Response.BinaryWrite(bytes); } Response.Flush();
Response.End(); }首先数组最大长度为int.MaxValue,接着正常程序是不会分那么大内存,很容易搞挂服务项目器(也是能浏览的文档,极限值最多也就2G不到)【不推荐】第二种:(Response.WriteFile)。
public void FileDownload3(){ string fileName = "增建文档夹2.rar";//顾客端留存的文档名 string filePath = Server.MapPath("/App_Data/增建文档夹2.rar");//要被浏览的文档方向
FileInfo fileInfo = new FileInfo(filePath); Response.Clear(); Response.ClearContent(); Response.ClearHeaders();
Response.AddHeader("Content-Disposition", "attachment;filename=\"" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8) + "\"");
Response.AddHeader("Content-Length", fileInfo.Length.ToString());//文档大小不一 Response.AddHeader("Content-Transfer-Encoding", "binary");
Response.ContentType = "application/octet-stream"; Response.WriteFile(fileInfo.FullName);//大小不一参数必须介于零和最大的 Int32 值间(也是最大2G,不过这个操作非常耗内存)
//这儿容易内存溢出 Response.Flush(); Response.End();}问题和第一种类似,也是不能浏览大于2G的文档接着浏览差不多2G文档时,机器也是处在被挂的边缘,相当恐怖。
【不推荐】第三种:(Response.OutputStream.Write)public void FileDownload4(){ string fileName = "大统计数据.rar";//顾客端留存的文档名
string filePath = Server.MapPath("/App_Data/大统计数据.rar");//要被浏览的文档方向 if (System.IO.File.Exists(filePath))
{ const long ChunkSize = 102400; //100K 每次加载文档,只加载100K,这样能缓解服务项目器的压力 byte[] buffer = new byte[ChunkSize];
Response.Clear(); using (FileStream fileStream = System.IO.File.OpenRead(filePath)) {
long fileSize = fileStream.Length; //文档大小不一 Response.ContentType = "application/octet-stream"; //二进制流
Response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8));
Response.AddHeader("Content-Length", fileStream.Length.ToString());//文档总大小不一 while (fileSize > 0 && Response.IsClientConnected)//推论顾客端是否还连接了服务项目器
{ //实际加载的大小不一 int readSize = fileStream.Read(buffer, 0, Convert.ToInt32(ChunkSize));
Response.OutputStream.Write(buffer, 0, readSize); Response.Flush();//如果顾客端 中止浏览时,这儿会阻塞。
fileSize = fileSize - readSize;//文档剩余大小不一 } } Response.Close();
}}这儿明显看到了是在循环加载输出,比较机智浏览大文档时没压力【推荐】第四种:(Response.TransmitFile)也就上开始举例说的那种,浏览大文档也没压力【推荐】public void FileDownload5()。
{ //后面能做采用者登入校正、采用者职权校正等 string filename = "大统计数据.rar"; //顾客端留存的文档名 string filePath = Server.MapPath("/App_Data/大统计数据.rar");//要被浏览的文档方向 。
Response.ContentType = "application/octet-stream"; //二进制流 Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
Response.TransmitFile(filePath); //将指定文档写入 HTTP 积极响应输出流}文档浏览-顾客端下面同时实现了文档浏览的服务项目端同时实现,接下来他们同时实现文档浏览的顾客端同时实现顾客端的浏览能间接是浏览器提供更多的浏览,也能是迅雷或者他们自己写的浏览程序。
这儿为了更好的分析,他们来用winfrom程序自己写个浏览顾客端间接浏览private async void button1_ClickAsync(object sender, EventArgs e)
{ using (HttpClient http = new HttpClient()) { var httpResponseMessage = await http.GetAsync("http://localhost:813/增建文档夹2.rar");//推送允诺 (镜像是a条码提供更多的)
var contentLength = httpResponseMessage.Content.Headers.ContentLength;//加载文档大小不一 using (var stream = await httpResponseMessage.Content.ReadAsStreamAsync())//加载文档流
{ var readLength = 1024000;//1000K 每次加载大小不一 byte[] bytes = new byte[readLength];
int writeLength; while ((writeLength = stream.Read(bytes, 0, readLength)) > 0)//分块加载文档流
{ using (FileStream fs = new FileStream(Application.StartupPath + "/temp.rar", FileMode.Append, FileAccess.Write))//采用追加方式打开两个文档流
{ fs.Write(bytes, 0, writeLength);//追加写入文档 contentLength -= writeLength;
if (contentLength == 0)//如果写入完成 给出提示 MessageBox.Show("浏览完成");
} } } } }看着那么漂亮的标识符,好像没问题。可现实往往事与愿违。
他们看到了两个异常“System.Net.Http.HttpRequestException:“不能向缓冲区写入比所配置最大缓冲区大小不一 2147483647 更多的字节”,什么鬼,又是2147483647这个数字。
因为他们浏览的文档大小不一超过了2G,无法缓冲浏览可是“缓冲浏览”下又是什么鬼我也不知道那他们试试能关掉这个东东呢?答案是肯定的var httpResponseMessage = await http.GetAsync("http://localhost:813/增建文档夹2.rar");//推送允诺。
改成下面就能了var httpResponseMessage = await http.GetAsync("http://localhost:813/增建文档夹2.rar",HttpCompletionOption.ResponseHeadersRead);
//积极响应一可用且标题可读时即应完成的操作。 (尚未加载的内容。)
他们看到枚举HttpCompletionOption的两个值两个是积极响应加载内容,两个是积极响应加载标题(也是Headers里的内容)异步浏览他们发现在浏览大文档的这时候会造成界面假死这是UI单线程程序的通病。
当然,那么差的采用者体验是他们不能容忍的下面他们为浏览开两个线程,避免造成UI线程的阻塞/// /// 异步浏览/// ///
/// private async void button2_ClickAsync(object sender, EventArgs e){ //开启两个异步线程
await Task.Run(async () => { //异步操作UI元素 label1.Invoke((Action)(() => {
label1.Text = "准备浏览..."; })); long downloadSize = 0;//已经浏览大小不一
long downloadSpeed = 0;//浏览速度 using (HttpClient http = new HttpClient()) { var httpResponseMessage = await http.GetAsync("http://localhost:813/增建文档夹2.rar", HttpCompletionOption.ResponseHeadersRead);//推送允诺
var contentLength = httpResponseMessage.Content.Headers.ContentLength; //文档大小不一
using (var stream = await httpResponseMessage.Content.ReadAsStreamAsync()) { var readLength = 1024000;//1000K
byte[] bytes = new byte[readLength]; int writeLength; var beginSecond = DateTime.Now.Second;//当前时间秒
while ((writeLength = stream.Read(bytes, 0, readLength)) > 0) { //采用追加方式打开两个文档流
using (FileStream fs = new FileStream(Application.StartupPath + "/temp.rar", FileMode.Append, FileAccess.Write))
{ fs.Write(bytes, 0, writeLength); } downloadSize += writeLength;
downloadSpeed += writeLength; progressBar1.Invoke((Action)(() =>
{ var endSecond = DateTime.Now.Second; if (beginSecond != endSecond)//计算速度
{ downloadSpeed = downloadSpeed / (endSecond - beginSecond);
label1.Text = "浏览速度" + downloadSpeed / 1024 + "KB/S"; beginSecond = DateTime.Now.Second;
downloadSpeed = 0;//清空 } progressBar1.Value = Math.Max((int)(downloadSize * 100 / contentLength), 1);
})); } label1.Invoke((Action)(() => {
label1.Text = "浏览完成"; })); } } });}效果图:
ease下面的方式他们发现,如果浏览到两个半断网了下次会重头开始浏览这和他们今天的主题明显不符嘛下面他们开始正式进入主题文档浏览之ease把后面他们说到的头特性Range用起来var request = new HttpRequestMessage { RequestUri = new Uri(url) };。
request.Headers.Range = new RangeHeaderValue(rangeBegin, null); //【关键点】全局变量记录已经浏览了多少,接着下次从这个边线开始浏览var httpResponseMessage = await http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);。
完整标识符:/// /// 是否中止/// static bool isPause = true;/// /// 浏览开始边线(也是已经浏览了的边线)
/// static long rangeBegin = 0; //(当然,这个值也能存为持久化如文本、统计数据库等)private async void button3_ClickAsync(object sender, EventArgs e)。
{ isPause = !isPause; if (!isPause)//点击浏览 { button3.Text = "中止"; await Task.Run(async () =>
{ //异步操作UI元素 label1.Invoke((Action)(() => { label1.Text = "准备浏览...";
})); long downloadSpeed = 0;//浏览速度 using (HttpClient http = new HttpClient())
{ var url = "http://localhost:813/增建文档夹2.rar"; var request = new HttpRequestMessage { RequestUri = new Uri(url) };
request.Headers.Range = new RangeHeaderValue(rangeBegin, null); //【关键点】全局变量记录已经浏览了多少,接着下次从这个边线开始浏览。
var httpResponseMessage = await http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
var contentLength = httpResponseMessage.Content.Headers.ContentLength;//本次允诺的内容大小不一 if (httpResponseMessage.Content.Headers.ContentRange != null) //如果为空,则说明服务项目器不全力支持ease
{ contentLength = httpResponseMessage.Content.Headers.ContentRange.Length;//服务项目器上的文档大小不一
} using (var stream = await httpResponseMessage.Content.ReadAsStreamAsync())
{ var readLength = 1024000;//1000K byte[] bytes = new byte[readLength];
int writeLength; var beginSecond = DateTime.Now.Second;//当前时间秒
while ((writeLength = stream.Read(bytes, 0, readLength)) > 0 && !isPause) {
//采用追加方式打开两个文档流 using (FileStream fs = new FileStream(Application.StartupPath + "/temp.rar", FileMode.Append, FileAccess.Write))
{ fs.Write(bytes, 0, writeLength); }
downloadSpeed += writeLength; rangeBegin += writeLength;
progressBar1.Invoke((Action)(() => { var endSecond = DateTime.Now.Second;
if (beginSecond != endSecond)//计算速度 { downloadSpeed = downloadSpeed / (endSecond - beginSecond);
label1.Text = "浏览速度" + downloadSpeed / 1024 + "KB/S"; beginSecond = DateTime.Now.Second;
downloadSpeed = 0;//清空 } progressBar1.Value = Math.Max((int)((rangeBegin) * 100 / contentLength), 1);
})); } if (rangeBegin == contentLength)
{ label1.Invoke((Action)(() => { label1.Text = "浏览完成";
})); } } } }); } else//点击中止
{ button3.Text = "继续浏览"; label1.Text = "中止浏览"; }}效果图:
到现在为止,你以为他们的ease就完成了吗?错,你呢发现他们采用的浏览镜像是a条码的也是他们自己写服务项目端提供更多的浏览镜像呢也能全力支持ease呢?下面我换个浏览镜像试试便知ease(服务项目端的全力支持)。
测试结果如下:
发现并不全力支持ease为什么a条码镜像能间接全力支持,他们写的浏览却不全力支持呢a条码的镜像指向的间接是iis上的文档(iis预设全力支持),而他们写的却没做积极响应数据流表头Range的处理(没想象中的那么智能嘛 >_<)。
后面他们说过,断线续传是HTTP的两个协定他们严格遵守它,它就存有,他们不严格遵守它也就不存有那下面他们修改后面的文档浏览标识符(服务项目端):public void FileDownload5(){ 。
//后面能做采用者登入校正、采用者职权校正等 string filename = "大统计数据.rar"; //顾客端留存的文档名 string filePath = Server.MapPath("/App_Data/大统计数据.rar");//要被浏览的文档方向 。
var range = Request.Headers["Range"]; if (!string.IsNullOrWhiteSpace(range))//如果严格遵守协定,全力支持ease {
var fileLength = new FileInfo(filePath).Length;//文档的总大小不一 long begin;//文档的开始边线 long end;//文档的结束边线
long.TryParse(range.Split(=)[1].Split(-)[0], out begin); long.TryParse(range.Split(-)[1], out end);
end = end - begin > 0 ? end : (fileLength - 1);// 如果没结束边线,那他们读剩下的全部 //表头 表明 浏览文档的开始、结束边线 和文档总大小不一
Response.AddHeader("Content-Range", "bytes " + begin + "-" + end + "/" + fileLength); Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment;filename=" + filename); Response.TransmitFile(filePath, begin, (end - begin));//推送 文档开始边线加载的大小不一
} else { Response.ContentType = "application/octet-stream"; Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
Response.TransmitFile(filePath); }}接着再测试ease,完美全力支持多线程同时浏览(分片浏览)文档的ease已经分析完了不过中间有些细节的小东西你能根据实际需求去完善。
如:文档命名、ease的文档是否发生了改变、浏览完成后校正文档和服务项目器上的是否一致还有他们能根据表头特性Range来同时实现多线程浏览,不过这儿就不贴标识符了,贴个效果图吧和上一篇文档上传里的多线程上传同理。
|