QA@IT

ASP.NET で、複数の画像の取得・プレビュー表示をした上、サーバにアップロードしたい

20993 PV

【開発環境】
Windows8.1 IIS 8.5
Microsoft Visual Studio Express 2012 for Web

【テスト・稼働環境】
サーバは、WindowsServer2012 R2
端末
iPad OS 8.1.2
iPhone OS 8.1.2
ブラウザは、当面Safariを考えています。
他、Androidも考えています。

【したいこと】
1) ブラウザ上のボタン「撮影済選択」を押すと、iPhoneの「写真」が開き、その中の「カメラロール」等から、画像を複数選択します。
2) 複数選択後は、選択した画像が、複数プレビュー表示されます。
3) 1)、2)とは別に、「撮影」ボタンを押すと「カメラ」が起動し、撮影するとそれがプレビュー(サムネイル)表示されます。
4) 3)を繰り返すと、その分だけ、カメラ起動→撮影→撮影した画像のプレビュー表示がされます。
5) プレビューされている画像で、何らかの×ボタンを押したり、もしくはチェックボックスの操作等で、アップロードを取りやめることができるようにする。
6) 「保存」ボタンを押すと、5)で指定した画像がサーバの特定のフォルダ「\svr\hoge」にアップロードされます。
アップロードしたい画像は縮小されたデータではなく、選択・撮影されたデータそのものです。
7) 既存の画像データを開く「撮影済選択」ボタンと「撮影」ボタンは、別の機能であり別のボタンが望ましいですが、「選択」ボタンを押したあと、「写真またはビデオを撮る」「フォトライブラリ」に自動的に分岐して、そこから操作が進むようになっても良いです。
8) できれば、5)まではサーバとの通信無しでブラウザのみで完結できると良いです。

【作成の経過とご質問】
1), 2)はできたので、まずは 6)のように、「保存」ボタンでサーバに保存したいのですが、どのようにコードを書けば良いでしょうか。(「保存」ボタンはASP.netのボタンです。)
コード部分の ” posted = Request.Files("files[0]")” で、複数表示されている画像のうちの1枚目が取得できるかと思ったのですが、postedがNothingとなってしまい取得できません。

【ソース部分】

<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="test.aspx.vb" Inherits="Test.test" %>
  <style type="text/css">

      #upload {
        width:90px;
        height:35px;
        text-align:center;
        line-height:35px;
        border-radius:5px;
        color:#fff;
        background-color:#4385bf;
        margin:10px 0;
        cursor:pointer;
      }
      #results {
        border:1px solid #4385bf;
        background-color:#bbd4ea;
        height:160px;
        width:500px;
        padding:5px;
        margin:10px 0;
      }
      #results img {
        border:1px solid #000;
        margin:0 5px 0 0;
      }
      #file {
        opacity:0;
      }

      </style>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>

    <form id="form1" runat="server">
            <input type="file" accept="image/jpeg, image/gif, image/png" id="file" name="files[]" multiple>

       <%--     <button type="submit">submit</button>  --%>

    <div>

        <div id="upload">撮影済選択</div>
        <div id="results"></div>
        <script type="text/javascript" src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
        <script>
            $(function () {
                $('#upload').click(function () {
                    $('#file').click();
                    $('#file').on('change', setImage);
                });

                function setImage() {
                    for (var i = 0; i < this.files.length; i++) {
                        var file = this.files[i];
                        fr = new FileReader();
                        fr.onload = function (e) {
                            var img = $('<img>');
                            img.attr('src', e.target.result);
                            img.css('height', '160px', 'width', '100px');
                            img.css('width', '160px');
                            $('#results').append(img);
                        };
                        fr.readAsDataURL(file);
                    }
                }
            });

        </script>

    </div>

        <asp:Button ID="Button1" runat="server" Text="保存" />
    </form>

</body>
</html>

【コード部分】

Public Class test
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    End Sub

    Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        Dim posted As HttpPostedFile
        Dim SendPath As String = "\\svr\hoge"

        posted = Request.Files("files[0]")

        If Not posted.FileName = "" Then
            posted.SaveAs(SendPath & "1.JPG")
        End If

    End Sub
End Class
  • 端末が iPad OS 8.1.2, iPhone OS 8.1.2 と言われると自分には検証する手段がないのでお手上げですが、Windows 上の Safari で動けば OK ということなら考えて見ますが、いかがでしょう? -
  • Windowsに私もSafariをインストールして試したところ、画像選択後に、画像が表示できませんでした。「設定→セキュリティ→Javascriptを有効にする」にはチェックが入っているのですが…。
    ただ、Windows上のChrome(ver34)、IE(ver11),Opera(ver26.0)では動作の確認ができています。
    -
  • なお、Windowsのブラウザ上で画像の複数選択のときは「ダイアログボックスが開いているときにCtrlキーを押しながらマウスで順次複数選択」という動作です。
    よろしくお願い致します。
    -
  • Windows の Safari 5.1 系はダメっぽいですね。```<input type="file" multiple="multiple" ... />``` が動きません。なので、検証に Wondows の Safari を使うのは意味がなかったです。すみません。 -
  • ちなみに、Ajax Control Toolkit の AjaxFileUpload コントロールも、ダウンロードサイトから入手できるデモで試した限りですが、Windows の Safari 5.1 系ではサーバーエラーで動きませんでした。 -
  • どうしてSafariだけがWindows上では効かないのかわかりませんが、確かにそうですね。 -

回答

質問にあがっている環境であれば HTML5環境になると思いますので、以下の様に修正すればいいと思います。

aspx部の修正

最初に本題と関係ないですが、

  • styleタグはheadタグの中に書いた方がいいでしょう。

  • name属性はあくまで名前なので内容が配列かどうかは関係ありません。なので記号は使わない方がいいでしょう。

そして、本題ですが uploadを行うので、formにenctype属性をつける必要があります。

こちらの別の質問での回答のように、Page_Loadなどで設定する方法もありますし、formタグに直接記述する方法もあります。

これらを直すと以下の様なaspxになります(styleとinputタグ以降は省略しています)。

<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="test.aspx.vb" Inherits="Test.test" %>
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <style type="text/css">
       ~省略~
    </style>
</head>
<body>
    <form id="form1" runat="server" enctype="multipart/form-data">
        <input type="file" accept="image/jpeg, image/gif, image/png" id="file" name="files" multiple>

~以下略~

VBコード部分の修正

保存ボタン(Button1)を押したイベントでどのように取得するかですが、

単純な取得方法としては、Request.FilesのAllKeysプロパティを利用する方法があります。

Requestされたすべてのファイルを、リクエストされたファイル名で保存するには以下の様にすればいいでしょう。

Request.Filesに引き渡す文字列はキーとなる文字列またはインデックスとなる数値であり、コントロールの名前ではない事に注意してください。

Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

    Dim SendPath As String = "\\svr\hoge\"  ' 最後に \ を付け足しました

    For Each key In Request.Files.AllKeys

        Dim file = Request.Files(key)
        file.SaveAs(SendPath & Path.GetFileName(file.FileName))
    Next
End Sub

(Path.GetFileNameはフルパスでファイル名を送信してくるブラウザ(IEなど)があるため、ファイル名だけを抽出しています。)

キー文字列ではなく、インデックスでループさせる場合は以下の様にします。

Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

    Dim SendPath As String = "\\svr\hoge\"  ' 最後に \ を付け足しました

    For i = 0 To Request.Files.Count - 1

        Dim file = Request.Files(i)
        file.SaveAs(SendPath & Path.GetFileName(file.FileName))
    Next
End Sub

ところで、現在はinput[type=file]が一つなのですべて取り出してしまえば問題ありませんが、
これがページ内に複数あった場合は分けて取得したくなると思います、そういう場合は以下の様にすれば個別に取得することができます。

<input type="file" accept="image/jpeg, image/gif, image/png" id="file" name="filesA" multiple>
<input type="file" accept="image/jpeg, image/gif, image/png" id="file" name="filesB" multiple>
    For Each file In Request.Files.GetMultiple("filesA")
        file.SaveAs(SendPath & "A_" & Path.GetFileName(file.FileName))
    Next

    For Each file In Request.Files.GetMultiple("filesB")
        file.SaveAs(SendPath & "B_" & Path.GetFileName(file.FileName))
    Next

この時に渡す文字列はname属性に指定した文字列です。


参考までに、iPhone 6のChromeとsafariで試してみたところ、カメラロールから取得されたファイルのFileNameは全部同じ名前で送信されているようでした。

そして、AllKeysで取れるキーも同一となってしまうようだったので、取り出すときにはAllKeysではなく、インデックスかGetMultipleを使うようにしないと思うような動作にならないかもしれません。

さらに保存の際にも通番などを振るといいでしょう。String.Formatなどでやった方がいいんですが、単純に文字列結合でやると以下の様な感じでしょうか。

    Dim sendPath = "\\svr\hoge\"  ' 最後に \ を付け足しました
    Dim count = 0

    For Each file In Request.Files.GetMultiple("files")
        count = count + 1
        file.SaveAs(SendPath & count & "_" & Path.GetFileName(file.FileName))
    Next

FileUploadコントロールを使う場合

余談になりますが FileUploadコントロールでも複数ファイルの送信は可能です。

複数のファイルを送信する場合はAllowMultiple="true"を指定します。

    <asp:FileUpload ID="fup" runat="server" AllowMultiple="true" />

コード側(Button1_Click)でファイルを取得するには、FileUploadコントロールのPostedFilesプロパティやHasFilesプロパティを使えばいいでしょう。

    For Each file In fup.PostedFiles
        file.SaveAs(SendPath & Path.GetFileName(file.FileName))
    Next

サーバー/アプリケーション設定で気を付けること

ASP.NETやIISではリクエストサイズ規制があるため、複数の写真を送信しようとするとエラーが発生する可能性があります。

asp_net_request_error.png

ASP.NETではhttpRuntime maxRequestLengthの設定(Web.Config)、

IISでは requestLimits, maxAllowedContentLength の設定にも気を付けてください。


最後に、対象がiOSやAndroidということであればHTML5対応ブラウザですので上記方法で問題ないとおもいますが、PCなどHTML5非対応ブラウザをターゲットに入れる場合はAjaxコントロールなどの利用を検討する必要があります。

編集 履歴 (0)
  • ご丁寧にありがとうございます。
    ① Allkeyで試したところ、Windowsからはできたのですが、iPhone 6の各ブラウザ(
    Safari,Chrome,OperaMini,Sleipnir)で試したところ、述べられている通りうまくいきませんでした。複数選択した画像のうち、最後の画像のみがフォルダに残ります。
    -
  • ② 同様に、" For i = 0 To Request.Files.Count - 1 …"で試したところ、同様の結果となりました。 -
  • iPhone 6のブラウザでは、ファイル名が一律で同じ"image.jpg"になっていましたが、
    "For i = 0 To Request.Files.Count - 1
    Dim file = Request.Files(i)"
    -
  • " file.SaveAs(SendPath & "_" & CStr(i) & Path.GetFileName(file.FileName)) Next "
    というふうに、カウンタ i を保存する名前にに入れることで解決いたしました。
    -
  • そうですね、一応私の回答でもそうしてあります。「SendPath & count & "_" & Path.GetFileName(file.FileName)」(countがカウンタ) -

とりあえず分かるところを回答します。

コード部分の ” posted = Request.Files("files[0]")” で、複数表示されている
画像のうちの1枚目が取得できるかと思ったのですが、postedがNothingとなってしま
い取得できません。

form 要素に enctype="multipart/form-data" の設定がないからだと思います。

ASP.NET サーバーコントロールの FileUpload をページに配置すると、ASP.NET がブラウザに送信する html コードをレンダリングする際、form 要素に enctype="multipart/form-data" を追加してくれますが、普通の <input type="file" ... /> 要素ではそうなりません。

詳しくは以下のページを見てください。

FileUpload と form 要素 の enctype
http://surferonwww.info/BlogEngine/post/2015/01/03/fileupload-control-and-enctype-attribute-of-form-element.aspx

「保存」ボタンでサーバに保存したいのですが、どのようにコードを書けば良いでしょうか。

FileUpload クラスを使うことでよければ、MSDN ライブラリにサンプルがありますので、見てください。

FileUpload クラス
http://msdn.microsoft.com/ja-jp/library/system.web.ui.webcontrols.fileupload(v=vs.110).aspx

あと、複数ファイルを同時にアップロードしようとしているようですが、それは FileUpload コントロールではできません。(訂正します・・・下記参照) Ajax Control Toolkit の AjaxFileUpload コントロールを利用すれば可能ですが、端末が iPad OS 8.1.2, iPhone OS 8.1.2 で使用可能かどうかはわかりません。

#今、自分の環境で使えるのは Windows 上で動くブラウザのみ(IE9, Firefox 35 など)で、iPad OS 8.1.2, iPhone OS 8.1.2 を使ってどうなるかと聞かれても答えられませんのでご了承ください。

【訂正】

flied_onion さんのレスを見て調べなおしましたが、.NET Framework 4.5 以降では FileUpload コントロールに AllowMultiple プロパティ、 PostedFiles プロパティが追加されていました。ブラウザが HTML5 をサポートしていればそれが使えるようです。

編集 履歴 (2)
ウォッチ

この質問への回答やコメントをメールでお知らせします。