by WebSurfer
2015年4月1日 16:30
ダウンロードするファイルをサーバー側で作成するのに時間がかかる場合、UpdatePanel と UpdateProgress コントロールを使って、ファイル作成中であることをユーザーに知らせる方法を書きます。
上の画像の一番上の行が表示された UpdateProgress です。この記事の例では、UpdateProgress が表示されるのはファイルをダウンロードしている時ではなく、その前のサーバー側でファイルを作成している時なのでご注意ください。
先の記事「ダウンロードは別ウィンドウで」に書いたような方法では、親ウィンドウの .aspx ページへの応答はすぐ帰ってくるものの、別ウィンドウを開いてそれから要求したダウンロード用 .aspx ページの応答はサーバー側でファイルの作成が完了するまで帰ってきません。
従って、ファイルの作成に時間がかかるとその間無反応になってしまい、ユーザーフレンドリーという面でどうかということになってしまいます。
少なくともサーバー側でファイルを作成している間その旨ユーザーに通知すれば、ユーザーのイライラも多少おさまるであろうということで、非同期要求と UpdateProgress を使った例を書いてみました。
どのような構成かを簡単に書くと、(1) .aspx ページに iframe と Button を含んだ UpdatePanel と UpdateProgress を配置、(2) Button クリックで非同期ポストバック、(3) サーバー側で Button クリックのハンドラでファイルを作成、(4) iframe の src 属性に作成したファイルを取得してダウンロードする HTTP ハンドラを設定・・・ということです。
そのようにすれば、非同期ポストバックをかけてから応答が帰ってくるまで UpdateProgress が表示され、応答が帰ってきて UpdatePanel 内が再描画されると、その中の iframe が src 属性に設定された HTTP ハンドラを要求し、HTTP ハンドラによってファイルがダウンロードされるという仕組みが作れます。
その .aspx ページのコード例は以下の通りです。
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void Button_Click(object sender, EventArgs e)
{
// 時間のかかるファイルの作成
System.Threading.Thread.Sleep(5000);
// ファイルの作成が完了したら、UpdatePanel 内に配置し
// た隠し iframe の src 属性に、作成したファイルを取
// 得してダウンロードする HTTP ハンドラを設定する。
// iframe には runat="server" 属性を付与してサーバー
// コントロールにしている点に注意。非同期ポストバック
// で UpdatePanel 内が再描画されると iframe から HTTP
// ハンドラが要求されファイルがダウンロードされる。
iframeDownload.Attributes["src"] =
"0104-TextFileDownloadHandler.ashx";
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script type="text/javascript">
//<![CDATA[
var manager;
function pageLoad(sender, args) {
if (args.get_isPartialLoad() === false) {
manager =
Sys.WebForms.PageRequestManager.getInstance();
manager.add_beginRequest(OnBeginRequest);
manager.add_endRequest(OnEndRequest);
}
}
function OnBeginRequest(sender, args) {
// 非同期要求の送信時に、アニメーションの表示などの
// スクリプトを起動する場合はここに設定。
}
function OnEndRequest(sender, args) {
// 完了時にスクリプトを起動する場合はここに設定。
}
// 実行中の非同期ポストバックを停止するスクリプト。
// UpdateProgress に配置したボタンの onclick に設定。
// サーバー側の処理まで停止されるわけではないので注意
function AbortPostBack() {
if (manager.get_isInAsyncPostBack()) {
manager.abortPostBack();
}
}
//]]>
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:UpdateProgress ID="UpdateProgress1" runat="server"
AssociatedUpdatePanelID="UpdatePanel1">
<ProgressTemplate>
<asp:Image ID="Image1" runat="server"
ImageUrl="~/Images/grid-loading.gif" />
しばらくお待ちください・・・
<input type="button" onclick="AbortPostBack()"
value="Cancel" />
<br /><br />
</ProgressTemplate>
</asp:UpdateProgress>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
UpdatePanel
<hr />
<%=DateTime.Now.ToString()%>
<br />
<asp:Button ID="Button1" runat="server"
Text="Download" OnClick="Button_Click" />
<br />
<%--ダウンロードに使う隠し iframe--%>
<iframe id="iframeDownload" runat="server"
style="visibility:hidden" />
</ContentTemplate>
</asp:UpdatePanel>
</form>
</body>
</html>
上のコード例には、実行中の非同期ポストバックを停止するためのスクリプトその他をオマケ(?)で加えておきました。UpdatePanel を使うと他にもいろいろできますので、興味がありましたら MSDN ライブラリの記事 PageRequestManager のイベントの処理 を見てください。
iframe の src 属性に設定する HTTP ハンドラのコード例は、先の記事「ダウンロードは HTTP ハンドラで」を見てください。
.aspx ページを使っても可能ですが、その記事に書きましたようにいろいろ問題がありますので、HTTP ハンドラを使ったほうがよさそうです。