本人是剛畢業(yè)進(jìn)入公司的菜鳥(niǎo)钱贯,關(guān)于asp.net和C#方面也完全是新手,寫(xiě)這個(gè)是作為開(kāi)發(fā)筆記锋谐,也希望給遇到類(lèi)似問(wèn)題的菜鳥(niǎo)參考坪哄,當(dāng)然歡迎任何人批評(píng)指正。挠轴。传睹。
#你需要一些基礎(chǔ)知識(shí)才能流暢的閱讀本文,本人在寫(xiě)這篇文章以及其描述的開(kāi)發(fā)的時(shí)候岸晦,已經(jīng)初步掌握了javascript欧啤、html睛藻、css、C#邢隧、web form開(kāi)發(fā)店印,另外還使用了Ajax技術(shù),此外還需要對(duì)jQuery和C#多線程概念有基本的了解
不想看我啰嗦倒慧,想直接看示例代碼的請(qǐng)直接滾動(dòng)至“腳本代碼”或者文章底部的完整示例項(xiàng)目下載鏈接
抱歉按摘,沒(méi)找到簡(jiǎn)書(shū)怎么實(shí)現(xiàn)頁(yè)內(nèi)跳轉(zhuǎn)
一、前言
前一段時(shí)間被安排了一個(gè)任務(wù)迫靖,需要在原來(lái)的頁(yè)面中加入一個(gè)上傳本地Excel表院峡,并將關(guān)鍵的幾列上傳到數(shù)據(jù)庫(kù)的功能。系宜。照激。。盹牧。俩垃。
一開(kāi)始是很順利的寫(xiě)出了大部分代碼,小問(wèn)題在網(wǎng)上也很容易找到解決方案汰寓,直到我寫(xiě)完功能代碼之后口柳,因?yàn)镋xcel表可能會(huì)非常龐大,導(dǎo)致后臺(tái)處理過(guò)程很耗時(shí)有滑,這時(shí)候前臺(tái)的web頁(yè)面是處于掛起的狀態(tài)跃闹,看起來(lái)就像卡了一樣,所以需要將后臺(tái)過(guò)程實(shí)時(shí)提示到頁(yè)面上毛好,對(duì)于這樣的功能望艺,絲毫沒(méi)有頭緒
我嘗試在隱藏代碼中,需要進(jìn)行步驟提示的地方加入Response.Write("");
語(yǔ)句肌访,然而結(jié)果是所有的顯示都是在隱藏代碼執(zhí)行完畢后找默,最后一起顯示到頁(yè)面上
在google上查到的原因是,線程問(wèn)題吼驶,默認(rèn)的.aspx頁(yè)面中所有的代碼都是在單線程中運(yùn)行的惩激,因此直到隱藏代碼完成執(zhí)行之后才會(huì)刷新頁(yè)面
貌似可以用
out.flush();
分塊逐步顯示刷新頁(yè)面,應(yīng)該是把原本的一個(gè)單線程分成了連續(xù)的多個(gè)單線程蟹演,和多線程的區(qū)別類(lèi)似于硬件電路中的串行和并行的區(qū)別风钻,個(gè)人理解,請(qǐng)指正
這種辦法貌似不能解決我的問(wèn)題轨帜,反正我是不知道怎么搞
參考鏈接:高性能WEB開(kāi)發(fā)(11) - flush讓頁(yè)面分塊,逐步呈現(xiàn)
問(wèn)題是我對(duì)C#和asp.net的多線程開(kāi)發(fā)技術(shù)也不懂啊魄咕,,蚌父,我TM是新手哮兰,你上來(lái)就讓我開(kāi)發(fā)多線程?苟弛?
事情一定不會(huì)這么狗血喝滞,而且,我只是想顯示個(gè)實(shí)時(shí)提示而已膏秫,難道還要從零開(kāi)始學(xué)多線程右遭。。缤削。窘哈。。亭敢。
直到我無(wú)意中看到了Ajax技術(shù)滚婉。。帅刀。然后有種让腹,果然顯示個(gè)實(shí)時(shí)提示是不需要從零開(kāi)始學(xué)習(xí)多線程的,哈哈哈哈哈
補(bǔ)個(gè)示例項(xiàng)目的目錄結(jié)構(gòu)
二扣溺、Ajax - post()方法
#下面僅介紹本文用到的內(nèi)容骇窍,想要深入了解請(qǐng)自行搜索“jQuery post”、“ajax post”锥余、“$.get $.post”等關(guān)鍵字
post()方法通過(guò)HTTP POST請(qǐng)求以異步的方式從服務(wù)器載入數(shù)據(jù)
jQuery中的$.post()方法貌似是$.ajax()方法的高層實(shí)現(xiàn)腹纳,我的理解就是進(jìn)一步封裝,簡(jiǎn)化了使用過(guò)程驱犹,當(dāng)然也會(huì)少一些功能嘲恍,比如沒(méi)有請(qǐng)求失敗后的執(zhí)行函數(shù)。着绷。蛔钙。palapala。荠医。吁脱。OK,$.post()方法的背景了解到此結(jié)束
本文使用的是這個(gè):$.post(url, data, success[callback]);
url彬向,就是使用post方法請(qǐng)求的服務(wù)器地址
data兼贡,在請(qǐng)求的同時(shí)發(fā)送給服務(wù)器的數(shù)據(jù),以key/value的形式娃胆,在服務(wù)器url的隱藏代碼中遍希,用Request.Form["key"]
接收
success[callback],服務(wù)器請(qǐng)求成功后的回調(diào)函數(shù)里烦,服務(wù)器url在成功運(yùn)行結(jié)束后會(huì)緊接著執(zhí)行回調(diào)函數(shù)
文章底部放了完整的項(xiàng)目下載鏈接凿蒜,需要的請(qǐng)直接滾動(dòng)至文章底部
腳本代碼
//script.js
function AjaxPostRequest(threadStatus, filePath)
{
//$.post(url, data, success[callback]);
$.post(
"AjaxPostServer.aspx", //Post參數(shù)禁谦,服務(wù)請(qǐng)求地址
{ //Post參數(shù),發(fā)向服務(wù)的參數(shù)废封,以key/value形式
status: threadStatus, //status=0,表示線程尚未創(chuàng)建州泊,status=1表示線程已創(chuàng)建
file: filePath //需要在后臺(tái)任務(wù)中進(jìn)行處理的Excel文件的路徑
},
function (data) { //Post參數(shù),成功后的回調(diào)函數(shù)
var backJson = eval('(' + data + ')'); //將json字符串解析為json對(duì)象
//頁(yè)面提示
$("#UploadLog2").html("后臺(tái)任務(wù)執(zhí)行到位置:" + backJson.position + "<br />" + backJson.massage);
//判斷漂洋,當(dāng)后臺(tái)任務(wù)還沒(méi)有運(yùn)行到結(jié)尾時(shí)遥皂,調(diào)用遞歸方法進(jìn)行實(shí)時(shí)狀態(tài)查詢(xún)
if (backJson.position != "5") {
AjaxPostRequest("1", filePath);
}
}
);
}
為了讓腳本代碼部分更加清晰,所以把另外兩種提示代碼單獨(dú)放在了下面刽漂,我嘗試向閱讀這篇文章的同學(xué)展示更多種頁(yè)面提示的方式演训,包括進(jìn)度條等等,也就是顯示實(shí)時(shí)提示和提示方式是無(wú)關(guān)的贝咙,我總共寫(xiě)了三種提示方式样悟,在文章底部的下載鏈接里包含的項(xiàng)目示例中包含了全部的代碼
//---------------------進(jìn)度條提示---------------------
$("#UploadPrograss").css({
"width": "500px",
"height": "16px",
"border": "1px solid gray"
});
$("#Prograss").css({
"height": "16px",
"left": "0px",
"top": "0px",
"background": "#46dd5a"
});
$("#Prograss").css({ "width": String(20 * parseInt(backJson.position)) + "%" })
//---------------------進(jìn)度條提示---------------------
//---------------------增量式進(jìn)度提示---------------------
if ("0" == backJson.position) {
$("#UploadLog").html("正在讀取本地Excel文件...");
}
else if ("1" == backJson.position) {
$("#UploadLog").html("本地Excel文件讀取完成。<br />正在解析本地Excel文件...");
}
else if ("2" == backJson.position) {
$("#UploadLog").html("本地Excel文件讀取完成颈畸。<br />本地Excel文件解析完成乌奇。<br />正在驗(yàn)證本地Excel文件...");
}
else if ("3" == backJson.position) {
$("#UploadLog").html("本地Excel文件讀取完成。<br />本地Excel文件解析完成眯娱。<br />本地Excel文件驗(yàn)證完成礁苗。<br />正在轉(zhuǎn)換本地Excel文件...");
}
else if ("4" == backJson.position) {
$("#UploadLog").html("本地Excel文件讀取完成。<br />本地Excel文件解析完成徙缴。<br />本地Excel文件驗(yàn)證完成试伙。<br />本地Excel文件轉(zhuǎn)換完成。<br />正在上傳本地Excel文件...");
}
else if ("5" == backJson.position) {
$("#UploadLog").html("本地Excel文件讀取完成于样。<br />本地Excel文件解析完成疏叨。<br />本地Excel文件驗(yàn)證完成。<br />本地Excel文件轉(zhuǎn)換完成穿剖。<br />本地Excel文件上傳完成蚤蔓。<br />");
}
//---------------------增量式進(jìn)度提示---------------------
下面是三種提示方式的簡(jiǎn)單效果,好吧糊余,確實(shí)很簡(jiǎn)陋秀又,但是這只是作為演示來(lái)用還是夠的,哈哈哈
以下是寫(xiě)這部分內(nèi)容時(shí)參考的鏈接
http://www.w3school.com.cn/jquery/ajax_post.asp
https://api.jquery.com/jQuery.post/
jQuery $.post $.ajax用法
淺談HTTP中Get與Post的區(qū)別
三贬芥、頁(yè)面和隱藏代碼
文章底部放了完整的項(xiàng)目下載鏈接吐辙,需要的請(qǐng)直接滾動(dòng)至文章底部
頁(yè)面代碼
//default.aspx
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script src="js/jquery-1.3.1.js" type="text/javascript"></script>
<script src="js/script.js" type="text/javascript"></script>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:FileUpload ID="FileUpload1" runat="server" />
<asp:Button ID="Upload" runat="server" OnClick="Upload_Click" Text="上傳Excel" />
<div id="FileExtTips" runat="server" style="color:Red">請(qǐng)選擇以.xls和.xlsx為后綴的Excel文件</div>
<br /><br />
<div id="UploadPrograss"><div id="Prograss"></div></div>
<br />
<div id="UploadLog"></div>
<br />
<div id="UploadLog2"></div>
</div>
</form>
</body>
隱藏代碼
//default.aspx.cs
using System;
using System.IO;
namespace RealTimeTips
{
public partial class _default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Upload_Click(object sender, EventArgs e)
{
// 檢查FileUpload1控件是否上傳正確格式的文件
string fileExt = Path.GetExtension(FileUpload1.FileName).ToLower();
if (".xlsx" != fileExt && ".xls" != fileExt)
{
FileExtTips.Visible = true;
return;
}
Directory.CreateDirectory(Server.MapPath("~/ExcelTemp/"));
string filePath = Server.MapPath("~/ExcelTemp/") + FileUpload1.FileName;
FileUpload1.SaveAs(filePath);
FileExtTips.Visible = false;
//調(diào)用腳本創(chuàng)建新的線程用以執(zhí)行后續(xù)任務(wù)
filePath = filePath.Replace(@"\", @"\\");//傳送到j(luò)s的字符串會(huì)被二次轉(zhuǎn)義,因此需要在傳送前進(jìn)行處理
ClientScript.RegisterStartupScript(ClientScript.GetType(), "", "<script>AjaxPostRequest('0', '" + filePath + "');</script>");
}
}
}
四蘸劈、服務(wù)器URL代碼
這部分是實(shí)現(xiàn)多線程的主體昏苏,當(dāng)然主要后臺(tái)任務(wù)也是放在這部分完成的
服務(wù)器頁(yè)面AjaxPostServer.aspx被設(shè)計(jì)為兩部分,一部分使用多線程技術(shù)用來(lái)實(shí)現(xiàn)異步線程,也就是新建立一個(gè)線程并將ThreadTask()函數(shù)放到新建立的線程中運(yùn)行
這樣只要把我們想在異步線程中完成的任務(wù)放到ThreadTask()函數(shù)中就行了
另外一部分則是在主線程中調(diào)用ThreadResponse()函數(shù)贤惯,用于查詢(xún)Session["log"]的內(nèi)容洼专,Session["log"]則是用來(lái)實(shí)時(shí)傳遞線程間的消息
這樣只要在一開(kāi)始調(diào)用線程創(chuàng)建的部分,之后不停的遞歸調(diào)用服務(wù)器頁(yè)面的ThreadResponse()函數(shù)就可以實(shí)時(shí)顯示后臺(tái)進(jìn)度了
AjaxPostServer.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="AjaxPostServer.aspx.cs" Inherits="AjaxPostServer"%>
AjaxPostServer.aspx.cs
using System;
using System.IO;
using System.Threading;
public partial class AjaxPostServer : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if ("0" == Request.Form["status"])
{
//線程未創(chuàng)建救巷,調(diào)用初始化函數(shù)
InitialThread();
}
else if ("1" == Request.Form["status"])
{
//線程已存在壶熏,進(jìn)行狀態(tài)查詢(xún)
if (null != Session["log"] && "" != Session["log"].ToString())
{
Response.Write(Session["log"].ToString());
}
}
}
private void InitialThread()
{
Session["log"] = ""; //初始化Session["log"]句柠,用于傳遞線程間消息
Thread newThread = new Thread(new ParameterizedThreadStart(ThreadTask)); //帶參數(shù)的線程實(shí)例化
newThread.Start(Request.Form["file"]); //啟動(dòng)線程并傳入Excel文件路徑
Response.Write("{'position':'0','massage':''}"); //初始化json字符串
}
//用于完成主要的后臺(tái)任務(wù)
private void ThreadTask(object filePath)
{
//對(duì)filePath路徑下的Excel文件進(jìn)行本地處理浦译,可以上傳數(shù)據(jù)到數(shù)據(jù)庫(kù),將想要實(shí)時(shí)顯示到頁(yè)碼上的數(shù)據(jù)放入到Session["log"]中溯职,遞歸調(diào)用的JS函數(shù)會(huì)實(shí)時(shí)將結(jié)果刷新到頁(yè)面上
Session["log"] = "{'position':'0','massage':'這里用于顯示消息提示或者錯(cuò)誤詳情'}";
Thread.Sleep(1000); //延時(shí)1s精盅,便于觀察狀態(tài)變化
Session["log"] = "{'position':'1','massage':'這里用于顯示消息提示或者錯(cuò)誤詳情'}";
Thread.Sleep(1000); //延時(shí)1s,便于觀察狀態(tài)變化
Session["log"] = "{'position':'2','massage':'這里用于顯示消息提示或者錯(cuò)誤詳情'}";
Thread.Sleep(1000); //延時(shí)1s谜酒,便于觀察狀態(tài)變化
Session["log"] = "{'position':'3','massage':'這里用于顯示消息提示或者錯(cuò)誤詳情'}";
Thread.Sleep(1000); //延時(shí)1s叹俏,便于觀察狀態(tài)變化
Session["log"] = "{'position':'4','massage':'這里用于顯示消息提示或者錯(cuò)誤詳情'}";
Thread.Sleep(1000); //延時(shí)1s,便于觀察狀態(tài)變化
Session["log"] = "{'position':'5','massage':'后臺(tái)任務(wù)執(zhí)行完成僻族!'}";
File.Delete(filePath.ToString()); //任務(wù)完成后刪除保存在服務(wù)器的Excel文件
}
}
事實(shí)上粘驰,Session["log"]的變化能夠被實(shí)時(shí)顯示到頁(yè)面上,是因?yàn)镾ession["log"]變化是在另一個(gè)線程中發(fā)生的述么,而這個(gè)時(shí)候主線程則是在不停的遞歸調(diào)用進(jìn)行狀態(tài)查詢(xún)并將結(jié)果刷新到頁(yè)面上蝌数,,度秘,從而實(shí)現(xiàn)了實(shí)時(shí)的狀態(tài)顯示效果顶伞,這就是為什么要把主要任務(wù)放在另一個(gè)線程去做的原因,用主線程來(lái)刷新頁(yè)面是很自然的做法
但是有個(gè)問(wèn)題剑梳,想要在后臺(tái)異步運(yùn)行的主要工作內(nèi)容和頁(yè)面之間傳遞信息唆貌,就要先穿過(guò)主頁(yè)面到達(dá)服務(wù)器頁(yè)面,然后從服務(wù)器頁(yè)面的主線程中到達(dá)服務(wù)器頁(yè)面創(chuàng)建的新線程中垢乙,這樣信息才能被主要任務(wù)接收到
所以我在考慮能不能直接在主頁(yè)面的隱藏代碼中開(kāi)啟一個(gè)異步線程用于完成后臺(tái)任務(wù)锨咙,這樣如果我想傳遞信息和數(shù)據(jù)只需要從隱藏代碼的主線程到達(dá)異步線程就行了
好吧,至少聽(tīng)上去簡(jiǎn)潔了很多追逮,哈哈哈
我在嘗試實(shí)現(xiàn)這個(gè)功能的時(shí)候酪刀,最早是因?yàn)榭吹搅讼旅孢@個(gè)帖子,但是后來(lái)發(fā)現(xiàn)帖子中的實(shí)現(xiàn)方法并不能完全滿足自己的要求羊壹,所以就推翻自己重寫(xiě)了蓖宦,雖然說(shuō)是重寫(xiě),但是在$.post方法的使用和思路上還是差不多的油猫。稠茂。。。睬关。诱担。不過(guò)人家的帖子寫(xiě)的簡(jiǎn)潔清晰,不像我這么啰嗦电爹,在這里感謝以及推薦蔫仙,哈哈哈哈
ASP.NET 多線程 監(jiān)控任務(wù)執(zhí)行情況,并顯示進(jìn)度條
RealTimeTips.zip項(xiàng)目下載
鏈接: https://pan.baidu.com/s/1o8Dl0pG 密碼: dg49
寫(xiě)在最后
這樣的實(shí)現(xiàn)會(huì)造成大量信息傳遞時(shí)的操作相對(duì)繁瑣丐箩,比如我想從服務(wù)器頁(yè)面的異步線程中傳回大量的數(shù)據(jù)顯示到頁(yè)面中摇邦,需要用回調(diào)函數(shù)傳回json字符串,然后用js解析再進(jìn)行頁(yè)面更新屎勘。施籍。。還有我最開(kāi)始的做法是直接對(duì)excel文件流進(jìn)行處理和解析的概漱,但是為了將文件放在異步線程中去處理丑慎,只好把文件轉(zhuǎn)存在服務(wù)器本地,把保存的地址傳送到異步線程中瓤摧,然后再讀取和解析竿裂。。照弥。腻异。。产喉。等等
當(dāng)然如果服務(wù)器的異步線程中只需要進(jìn)行類(lèi)似于上傳數(shù)據(jù)到數(shù)據(jù)庫(kù)之類(lèi)的操作捂掰,則不需要考慮大量信息傳遞的事情了。曾沈。这嚣。
我現(xiàn)在嘗試直接在主頁(yè)面的隱藏代碼中使用異步線程來(lái)顯示實(shí)時(shí)頁(yè)面提示,我不知道能不能做出來(lái)塞俱,如果可以的話姐帚,那應(yīng)該會(huì)有另外一篇在一個(gè)長(zhǎng)時(shí)間運(yùn)行的.aspx(C#)頁(yè)面中實(shí)時(shí)顯示后臺(tái)執(zhí)行過(guò)程和進(jìn)度(異步線程)這樣的文章。障涯。罐旗。。唯蝶。九秀。還請(qǐng)路過(guò)的大神指教
如果做不到,那我也進(jìn)一步學(xué)習(xí)了C#多線程的技術(shù)粘我,哈哈哈哈
//以下為2017年9月30號(hào)更新
最后的嘗試失敗了(我在嘗試解決問(wèn)題的過(guò)程中發(fā)現(xiàn)自己的思考方向可能是錯(cuò)誤的鼓蜒,然后我就放棄了痹换,我覺(jué)得這樣的嘗試并沒(méi)有太多意義)
我意識(shí)到自己可能犯了一個(gè)常識(shí)性的錯(cuò)誤
我在上面最后寫(xiě)到,希望能繞過(guò)JS直接在C#隱藏代碼中實(shí)現(xiàn)實(shí)時(shí)的頁(yè)面后臺(tái)進(jìn)度提示
但是問(wèn)題就在于都弹,我一開(kāi)始就沒(méi)完全搞懂娇豫,整個(gè)實(shí)現(xiàn)的機(jī)制
具體地說(shuō)就是實(shí)時(shí)頁(yè)面提示并不完全是線程問(wèn)題,還有服務(wù)器和客戶端的問(wèn)題
JS代碼是在本地的客戶端(瀏覽器)中運(yùn)行的畅厢,而C#隱藏代碼則是在服務(wù)器中運(yùn)行的
這就導(dǎo)致了冯痢,如果我想實(shí)時(shí)的刷新本地的瀏覽器頁(yè)面,那么使用JS來(lái)完成最后的頁(yè)面刷新工作才是最自然的做法
真實(shí)情況就是我根本就不知道如何在C#隱藏代碼中實(shí)時(shí)刷新頁(yè)面框杜,而在C#隱藏代碼中使用JS腳本注入的方式來(lái)實(shí)現(xiàn)浦楣,顯然是一件本末倒置的事(而且我也沒(méi)能成功)
我已經(jīng)在C#隱藏代碼中實(shí)現(xiàn)了多線程,線程間的參數(shù)傳遞也相對(duì)更加簡(jiǎn)單了霸琴,但是我卻沒(méi)辦法在C#隱藏代碼中把線程反饋的結(jié)果實(shí)時(shí)的刷新到頁(yè)面上
然后我才明白椒振,這件事本來(lái)就不是這個(gè)樣子的,所謂的常識(shí)性錯(cuò)誤則是指:試圖繞過(guò)JS代碼梧乘,用在服務(wù)器上運(yùn)行的C#代碼去刷新本地的客戶端頁(yè)面。庐杨。选调。。灵份。仁堪。
好吧,現(xiàn)在問(wèn)題清楚了填渠,實(shí)時(shí)頁(yè)面刷新弦聂,使用JS來(lái)實(shí)現(xiàn)最后的刷新步驟本來(lái)就是最自然的做法,上面用$.post(url, data, success[callback]);
氛什,就是很自然很好的實(shí)現(xiàn)辦法
反思
顯而易見(jiàn)的是莺葫,我一開(kāi)始沒(méi)能確切的弄清楚這件事,ASP.NET開(kāi)發(fā)和我原來(lái)所做過(guò)的Swift枪眉、C++開(kāi)發(fā)都不一樣捺檬,那就是并不是所有的代碼都是在一個(gè)地方運(yùn)行的,一部分運(yùn)行在本地的客戶端(瀏覽器)贸铜,另一部分則是在服務(wù)器上運(yùn)行的
我想作為一個(gè)稍有經(jīng)驗(yàn)的ASP.NET開(kāi)發(fā)者都是很清楚的知道這個(gè)的堡纬;作為新手來(lái)說(shuō),我確實(shí)在一開(kāi)始就知道這個(gè)蒿秦,然而真正去開(kāi)發(fā)的時(shí)候烤镐,我卻潛意識(shí)的忽略掉了這個(gè)問(wèn)題,潛意識(shí)仍然認(rèn)為所有的代碼都在一個(gè)機(jī)器上運(yùn)行
我不清楚我說(shuō)的這些是否正確棍鳖,如果有錯(cuò)誤炮叶,還請(qǐng)路過(guò)的同學(xué)指出,謝謝