最近のトラックバック

2017年12月
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            

Google AdSense2

« ASP.NET(C#)でファイルダウンロード | トップページ | ASP.NET(C#)でバイナリファイルをブラウザ表示 »

ASP.NET(C#)でファイルダウンロードしながら画面更新

ASP.NET(C#)でファイルダウンロードしながら画面更新を行うサンプルコードを作成してみた。



ネットでASP.NETでファイルダウンロードを調べると以下のようなサンプルコードが多く存在する。
しかし、このソースコードではファイルダウンロードと同時に画面更新はできない。


/// <summary>
/// ファイルダウンロードボタンクリック
/// </summary>
void btnDownloadFile_Click(object senddr, EventArg arg)
{
    //---------------------------------------------------------------------------------------------
    // テキストファイルをダウンロード
    //---------------------------------------------------------------------------------------------
    // ファイルパス設定
    string sFilePath = @"c:\temp.txt"";
    // HTTPヘッダー情報設定
    Response.AddHeader;
    // コンテンツタイプを設定
    Response.ContentType = "text/plain";
    // ファイル書込(データによりResponse.WriteFile()、Response.Write()、Response.BinaryWrite()を使い分ける。)
    Response.WriteFile(sFilePath);
    // レスポンス終了
    Response.End();
}


HTTPプロトコルの規約では1つのリクエスト(サーバ受信)に対し1つのレスポンス(サーバ応答)が基本である。
上記コードではファイルダウンロードにレスポンスを食いつぶしているが、画面更新を行うためにも別途レスポンスが必要である。
しかしボタンクリック等のポストバックでは1つのリクエストを2つのレスポンスに分割することはできない。

解決方法は、2つのリクエストを発行し、2つのリクエストを作ればよい。

具体的には以下の通り。
(A)ダウンロード呼び出しページとダウンロードページの2ページに分割する。
(B)ダウンロード呼び出しページのサーバ処理で「ダウンロードページを呼び出すクライアントスクリプト」をClientScript.RegisterStartupScript()等を使って登録する。
(C)ポストバック後に(B)のクライアントスクリプト経由でダウンロードページをリクエストする。

「ダウンロードページを呼び出すクライアントスクリプト」には以下の2つの方針がある。
①ダウンロードページを新しいウィンドウで開く。
②隠しiframeタグ内でダウンロードページを参照する。

しかし①②にも以下の長所・短所がある。
①は複数ファイルの同時ダウンロードが可能だが、ブラウザの種類・バージョンによって新しく開いたウィンドウが残ってしまう。
またブラウザの種類・バージョンによってはポップアップブロックされてしまうためダウンロードまでの操作が手間取る
②はウィンドウは表示されずセキュリティ規制もないが、複数ファイルの同時ダウンロードが出来ない。


以下のソースコードは「ダウンロード呼び出しページ(DownloadFileAndRefresh.aspx)」から「ダウンロードページ(DownloadPage.aspx)」を呼び出し、テキストファイルをダウンロードするサンプルである。
上述の「①ダウンロードページを新しいウィンドウで開く」「②隠しiframeタグ内でダウンロードページを参照する」の2つの方法で ダウンロードを行っている。

◆ダウンロード呼び出しページ(DownloadFileAndRefresh.aspx)
実行結果及びASP.NETソースコードの確認

<%@ Page Language="C#" Title="ファイルダウンロード&画面更新" AutoEventWireup="true" %>
<%@ Import Namespace="System.IO" %>
  :
  :
<script type="text/javascript">
    /// <summary>
    /// [javascript]ダウンロードページを新規ウィンドウで開く
    /// </summary>
    /// <param name="sUrl">ダウンロードページのURL</param>
    function OpenWindowDownloadPage(sUrl)
    {
        window.open(sUrl, "", "");
    }
    /// <summary>
    /// [javascript]ダウンロードページをiframeタグ内で開く
    /// </summary>
    /// <param name="sUrl">ダウンロードページのURL</param>
    function InsertIFrameDownloadPage(sUrl)
    {
        document.getElementById('ifDownloadPage').contentWindow.location.href = sUrl;
    }
</script>
<script type="text/C#" runat="server">
    /// <summary>
    /// 「例1 ダウンロードページを新規ウィンドウで開く」ボタンクリック
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void btnOpenWindowDownloadPage_Click(object sender, EventArgs e)
    {
        //-----------------------------------------------------------------------------------------
        // ファイルダウンロード
        // ダウンロードページを新規ウィンドウで開く
        //-----------------------------------------------------------------------------------------
        // コンテンツ文字列作成
        string sTextContents = string.Format("{0:HH:mm:ss}に「例1」ボタンをクリック", DateTime.Now);
        // コンテンツ文字列作成をURLエンコード
        sTextContents = HttpUtility.UrlEncode(sTextContents);
        // JavaScript作成
        string sScript = string.Format(
            "<script type='text/javascript'>OpenWindowDownloadPage('DownloadPage.aspx?text={0}&time={1:HHmmss}');<" + "/script>",
            sTextContents, DateTime.Now);
        // JavaScript登録
        ClientScript.RegisterStartupScript(GetType(), "ex1", sScript);
        //-----------------------------------------------------------------------------------------
        // ダウンロード時間をラベルに表示
        //-----------------------------------------------------------------------------------------
        lblDownloadTime.Text = DateTime.Now.ToString("HH:mm:ssに「例1ボタンからダウンロード」");
    }
    /// <summary>
    /// 「例2 隠しiframeタグ内にダウンロードページを開く」ボタンクリック
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void btnInsertIFrameDownloadPage_Click(object sender, EventArgs e)
    {
        //-----------------------------------------------------------------------------------------
        // ファイルダウンロード
        // 例2 隠しiframeタグ内にダウンロードページを開く
        //-----------------------------------------------------------------------------------------
        // コンテンツ文字列作成
        string sTextContents = string.Format("{0:HH:mm:ss}に「例2」ボタンをクリック", DateTime.Now);
        // コンテンツ文字列作成をURLエンコード
        sTextContents = HttpUtility.UrlEncode(sTextContents);
        // JavaScript作成
        string sScript = string.Format(
            "<script type='text/javascript'>InsertIFrameDownloadPage('DownloadPage.aspx?text={0}&time={1:HHmmss}');<" + "/script>",
            sTextContents, DateTime.Now);
        // JavaScript登録
        ClientScript.RegisterStartupScript(GetType(), "ex2", sScript);
        //-----------------------------------------------------------------------------------------
        // ダウンロード時間をラベルに表示
        //-----------------------------------------------------------------------------------------
        lblDownloadTime.Text = DateTime.Now.ToString("HH:mm:ssに「例2ボタンからダウンロード」");
    }
</script>
<h1>ファイルダウンロード&画面更新</h1>
<!-- コンテンツ説明 -->
<div class="contents_info">
    ASP.NETからファイルをダウンロードしながら画面を更新するサンプルです。<br />
</div>
<!-- コンテンツ -->
<asp:Button ID="btnOpenWindowDownloadPage" runat="server" 
        Text="例1 ダウンロードページを新規ウィンドウで開く" onclick="btnOpenWindowDownloadPage_Click" /><br />
<asp:Button ID="btnInsertIFrameDownloadPage" runat="server" 
        Text="例2 隠しiframeタグ内にダウンロードページを開く" 
        onclick="btnInsertIFrameDownloadPage_Click" /><br />
<br />
<asp:Label ID="lblDownloadTime" runat="server" />
<br />
<!-- 隠しiframe -->
<iframe id="ifDownloadPage" style="visibility:hidden"></iframe>
<br />
  :
  :


◆ダウンロードページ(DownloadPage.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">
    /// <summary>
    /// ページロード
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void Page_Load(object sender, EventArgs e)
    {
        string sDownLoadFileContents = "";  // ダウンロードファイルの内容
        //-----------------------------------------------------------------------------------------
        // コンテンツ取得
        // ※今回はQueryString経由でダウンロードファイルの内容を取得しているが、他に、
        //  以下の様な設定が考えられる。
        //  ①QueryStringにデータベースの主キーを設定、ダウンロードページ内で主キーを
        //   基にコンテンツ取得
        //  ②セッション経由でコンテンツ取得
        //-----------------------------------------------------------------------------------------
        if (Request.QueryString["text"] != null)
        {
            sDownLoadFileContents = HttpUtility.UrlDecode(Request.QueryString["text"]);
        }
        //-----------------------------------------------------------------------------------------
        // ダウンロード処理
        //-----------------------------------------------------------------------------------------
        // Response情報クリア
        Response.ClearContent();
        // バッファリング
        Response.Buffer = true;
        // HTTPヘッダー情報設定
        Response.AddHeader("Content-Disposition", "attachment;filename=download.txt");
        Response.ContentType = "text/plain";
        // コンテンツデータ書込
        Response.Write(sDownLoadFileContents);
        // フラッシュ
        Response.Flush();
        // レスポンス終了
        Response.End();
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>ダウンロードページ</title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>



  • ASP.NET(C#)でファイルダウンロード
  • ASP.NET(C#)でIEにOfficeドキュメントを表示
  • ASP.NET(C#)でIEのファイルダウンロードダイアログボタンをカスタマイズ
  • ASP.NET(C#)でバイナリファイルをブラウザ表示

  • « ASP.NET(C#)でファイルダウンロード | トップページ | ASP.NET(C#)でバイナリファイルをブラウザ表示 »

    ASP.NETサンプル」カテゴリの記事

    コメント

    すみません、質問があります。

    画面でボタン押下時に「しばらくお待ちください」のポップアップメッセージを出し
    Excel出力が終わったらポップアップを消す、という動作をさせたくて
    「例2 隠しiframeタグ内にダウンロードページを開く」
    の方法を参考にさせていただきました。

    Excelの出力もでき、ポップアップも消せたのですが
    出力処理の前には正常に動くJavaScriptが動かなくなってしまいます。

    JavaScriptは「あるCheckBoxにチェックを入れたら他のCheckBoxもONにする」
    といった、単純な処理を行っているものです。

    InsertIFrameDownloadPage('DownloadPage.aspx');
    がタグに追記されるせいなのか、
    何のせいなのかも分かっていない状況です。

    恐れ入りますがご教授願いませんでしょうか。

    K.Yさん

    当ブログの参照ありがとうございます。
    当記事を書いたniyoと申します。

    結論だけ言えば、原因・解決策はわかりません。
    また開発環境が手元になく検証もできません。
    私自身の知識もASP.NET3.5ベースで止まっています。


    その上での推論としての話です。
    ①このソース自体はASP.NET2.0ベースの知識で書いたものです。(サンプル作成・テストしたのはASP.NET3.5)
    ②ASP.NET3.5以降はAJAX的な処理がデフォルトですが、本ソースではボタンを押すとAJAXみたいな部分的なHTML・JavaScriptの書換えではなく、全HTML・JavaScriptの書換するポストバックが発生します。
    ③このため意図的またはASP.NETが自動生成した動的なJavaScript(CheckBox操作)が消えたことが原因で、本件が発生している可能性があります。
    ※推論なので、全く違う原因かもしれません。


    ■問題の切り分け
    開発中のブラウザのデバッグツール(IEなら開発者ツール・FirefoxならFirebug等)で、該当のJavaScript(CheckBox操作)が動かなくなった時点のHTMLやJavaScriptを参照し、該当のJavaScriptが存在するかどうか等を確認してみてください。
    該当のJavaScriptが見つからなければ、開発されているASP.NETのバージョンと本ソースの相性が悪いのでしょう。
    該当のJavaScriptがある場合は、もしかしたらASP.NETの自動生成コードが悪さをしているのかもしれません。window.alert()などでデバックコードをコツコツ書きながら、該当のJavaScriptが本当にクライアント環境で呼ばれているか等の確認をしてみてください。


    ■対処の例
    可能であればサーバーサイドのbtnInsertIFrameDownloadPage_Click()メソッド内の処理を全てJavaScriptで実行するようにし、ダウンロードするExcel作成のみサーバーサイドで行うようにソースコードを修正できれば、ASP.NETのバージョン等は全く関係なく動くと思います。
    ソースの書き換えができないようならば、デバッグツールと睨めっこしながら原因特定・解決する等をしてもらうしかありません。


    何はともあれ、本ソースはクライアントサイドとサーバーサイドが入り乱れている為、明確に切り分けながら、原因特定してください。


    一応ですが、以前作ったASP.NET2.0アプリで同じ論理のソースをつかい、アプリサーバの運用していたのですが、そのときはJavaScriptが動かないという現象は起きませんでした。


    あまりお役に立てそうになく申し訳ありませんが、よろしくお願いします。

    非常に丁寧なご回答、ありがとうございます。

    ボタン押下後の画面再描画時、JavaScriptのダウンロード前にExcelのダウンロードが実行されることによって不具合が発生していたようです。

    下記のようにしてExcelダウンロードのタイミングを遅延させることで回避できました。

    function InsertIFrameDownloadPage(sUrl) {
    setTimeout(function () {
    document.getElementById('ifDownloadPage').contentWindow.location.href = sUrl;
    }, 1000);
    }

    根本的な問題解決にはなっていないかもしれませんが
    とりあえずこれで様子を見ようと思います。

    ありがとうございました。

    コメントを書く

    (ウェブ上には掲載しません)

    トラックバック

    この記事のトラックバックURL:
    http://app.cocolog-nifty.com/t/trackback/532391/45559789

    この記事へのトラックバック一覧です: ASP.NET(C#)でファイルダウンロードしながら画面更新:

    « ASP.NET(C#)でファイルダウンロード | トップページ | ASP.NET(C#)でバイナリファイルをブラウザ表示 »

    Google AdSense


    • ---

    Amazon ウィジェット

    • ウィジェット

    @niyo_naのツイート

    無料ブログはココログ

    Google Analytics