[VB.NET] REST API⑬ ビューからWeb APIにファイルをアップロード

2021年10月7日

ビューからWeb APIにファイルをアップロードしてみましょう。
※ VisualStudio2019
APIプロジェクトの作成方法 についてはこちらを参照してください。

API用のコントローラーを追加

まずはAPI用のコントローラを追加します。
ソリューションエクスプローラで、「Controllers」を右クリックして表示されるメニューから、
「追加」>「コントローラー」を選択します。

このサンプルでは、空のコントローラーを使います。
「WebAPI」から、「Web API 2 コントローラー 空」を選択し、「追加」をクリックします。

コントローラーの名前を入力して、「追加」をクリックします。
名前は必ず「***Controller」としてください。 ※「***」の部分は任意です。
この例では、「ApiTestController 」としています 。

コントローラーに ファイルアップロード用のPOSTメソッドを実装

追加したコントローラー「ApiTestController 」にファイルアップロード用のPOSTメソッドを実装しましょう。

このサンプルでは「PostFileStream」というメソッド名にしました。
「HttpPost」と「ActionName(“PostFileStream")」という属性をメソッドに付けている点に注意してください。
後ほど、このメソッドを呼び出せるように WebApiConfig.vb にルートをマップするコードを追加します。
※メソッド名は「PostValue」でも構いません。その場合はWebApiConfig.vbの ルート追加はしなくてもよいです。

「PostFileStream」 メソッドでは、受け取ったファイルデータ(Stream)を「App_Data」に保存しています。
ファイルデータはStreamContentから一度MemoryStreamに読み出し、それをFileStreamにコピーして保存しています。
※直接FileStreamに読ませたかったのですが・・・うまくいきませんでした。出来ないのか、調べ方が悪いのか。

最後に、ビューに表示させたいメッセージを応答しています。
このメッセージがビューに表示され、アップロードしたファイルが保存できれば目的達成です。

コード

Imports System.Net
Imports System.Web.Http

Namespace Controllers
    Public Class ApiTestController
        Inherits ApiController

        ' POST: api/ApiTest/PostFileStream
        <HttpPost>
        <ActionName("PostFileStream")>
        Public Function PostFileStream(ByVal value As System.Net.Http.HttpRequestMessage) As System.Net.Http.HttpResponseMessage

            '保存先のファイルパス(App_Data)
            Dim path As String
            path = HttpContext.Current.Server.MapPath("~/App_Data/" & value.Content.Headers.ContentDisposition.FileName)


            Using mem As New System.IO.MemoryStream()
                '読み出し
                mem.Position = 0
                value.Content.ReadAsStreamAsync.Result.CopyTo(mem)

                'ファイルを保存
                Using sr As New System.IO.FileStream(path, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write)
                    mem.Position = 0
                    mem.CopyTo(sr)
                End Using
            End Using

            'レスポンスメッセージ作成
            Dim ret As System.Net.Http.HttpResponseMessage
            ret = New System.Net.Http.HttpResponseMessage(HttpStatusCode.OK)

            '成功メッセージを設定
            Dim content As New Net.Http.StringContent("The file was saved successfully !", Encoding.UTF8, "text/plain")
            ret.Content = content

            Return ret
        End Function
    End Class
End Namespace

次に、「App_Start」>「WebApiConfig.vb」のコードを開きます。
ここに、「 PostFileStream 」メソッドをマップするためのコードを追加します。

コード

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web.Http

Public Module WebApiConfig
    Public Sub Register(ByVal config As HttpConfiguration)
        ' Web API の設定およびサービス


        'jsonをブラウザで表示させる
        config.Formatters.JsonFormatter.SupportedMediaTypes.Add(New System.Net.Http.Headers.MediaTypeHeaderValue("text/html"))


        ' Web API ルート
        config.MapHttpAttributeRoutes()

        'ファイルアップロード用のAPI
        config.Routes.MapHttpRoute(
            name:="CustomApi",
            routeTemplate:="api/{controller}/{action}",
            defaults:=New With {.id = RouteParameter.Optional}
        )

        config.Routes.MapHttpRoute(
            name:="DefaultApi",
            routeTemplate:="api/{controller}/{id}",
            defaults:=New With {.id = RouteParameter.Optional}
        )
    End Sub
End Module

ビュー用のコントローラーを追加

続いてビュー用のコントローラを追加します。
ソリューションエクスプローラで、「Controllers」を右クリックして表示されるメニューから、
「追加」>「コントローラー」を選択します。

今度は「MVC」>「コントローラー」から、
「MVC 5 コントローラー 空」を選択し、「追加」をクリックします。

コントローラーの名前を入力して、「追加」をクリックします。
こちらも名前は必ず「***Controller」としてください。 ※「***」の部分は任意です。
この例では、「ViewTestController 」としています。

ビューのコントローラー「ViewTestController」に「upload」メソッドを実装します。
「ViewTest/upload」はメソッドを2つ用意します。

1つ目の 「ViewTest/upload」は、ビューを表示する際に呼び出されます。
このメソッドでは、メッセージ表示用のViewDataを初期化しています。

2つ目の 「ViewTest/upload」 はHttpPost属性が付いており、ビューからPOSTがおこなわれた際に呼び出されます。
ビューで選択したファイル情報をHttpPostedFileBaseから取り出してStreamContentを作成し、HttpClientとHttpResponseMessageを使用してAPIにPOSTリクエストをおこなっています。

コード

Imports System.Web.Mvc
Namespace Controllers
    Public Class ViewTestController
        Inherits Controller

        Function upload() As ActionResult
            ViewData("msg") = ""
            Return View()
        End Function

        <HttpPost()>
        <ValidateAntiForgeryToken>
        Function upload(ByVal post_file As HttpPostedFileBase) As ActionResult
            If (IsNothing(post_file)) Then
                ViewData("msg") = "ファイルを指定してください!"
            Else
                'レコード作成するAPIのURL
                Dim url As String
                url = DirectCast(Request, System.Web.HttpRequestWrapper).Url.GetLeftPart(UriPartial.Authority) & "/api/ApiTest/PostFileStream"

                'POST
                Dim result As System.Net.Http.HttpResponseMessage
                Dim clt As New Net.Http.HttpClient
                Dim content As Net.Http.StreamContent

                'ストリームコンテンツ
                content = New Net.Http.StreamContent(post_file.InputStream)

                'コンテンツタイプ
                content.Headers.ContentDisposition = New System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
                content.Headers.ContentType = New System.Net.Http.Headers.MediaTypeHeaderValue(post_file.ContentType)

                'ファイル名
                content.Headers.ContentDisposition.FileName = post_file.FileName

                'アップロード実行
                result = clt.PostAsync(url, content).Result

                Dim model As New ViewTestParam With {.Item1 = ""}
                'メッセージを取り出し
                ViewData("msg") = String.Format("{0} ({1})", result.Content.ReadAsStringAsync.Result, post_file.FileName)

            End If
            Return View()
        End Function
    End Class
End Namespace

ビュー(View)の追加

ビューを配置する フォルダー を用意します。
ソリューションエクスプローラで、「Views」フォルダーを右クリックして表示されるメニューから、
「追加」>「新しいフォルダー」を選択します。

作成したフォルダーの名前は「ViewTest」にします。

ビューを追加します。
ソリューションエクスプローラで、作成したばかりの「ViewTest」フォルダーを右クリックして
表示されるメニューから、「追加」>「ビュー」を選択します。

「MVC」>「表示」>「MVC5ビュー」を選択し、「追加」をクリックします。

ビュー名は「upload」とし、テンプレートは「Empty(モデルなし)」を選択して、「追加」をクリックします。

「ViewTest」フォルダーに「upload」という名前のビューが追加されます。
これで、「ViewTest/upload」というビューと、それを処理する「ViewTestController」というコントローラーが用意できました。
追加した「upload」ビューにファイルをアップロードするためのコードを書き加えます。

「input type="file"」でファイル選択のダイアログを表示して、ファイルを選択できるようにします。
この時、「name="post_file“」としている点に注意してください。
今回のサンプルでは、ビューのコントローラー「ViewTestController」の「upload」メソッドを実装した時に、

… Function upload(ByVal post_file As HttpPostedFileBase) As ActionResult
 
と、メソッドの引数を「 post_file 」という名前にしています。
この引数名とinputのnameを一致させることで、選択されたファイルの情報「upload」メソッドに渡すことができます。

コード

@Code
    ViewData("Title") = "upload"
End Code

<h2>upload</h2>

@Html.Label("message_label", ViewData("msg").ToString, New With {.style = "color:red;"})

<div>
    @Using (Html.BeginForm("upload", "ViewTest", FormMethod.Post, New With {.enctype = "multipart/form-data"}))
        @Html.AntiForgeryToken()
        @<input type="file" name="post_file" />
        @<input type="submit" value="アップロード" />

    End Using
</div>

実行して確認する

さっそく実行してみましょう。実行方法はこちらを参照してください。
アドレスバーに「/ViewTest/upload」と入力してEnterキーを押してページを移動してください。

アップロードページが表示されたら、「ファイルを選択」でファイルを選びます。
このサンプルでは、「up_test.csv」というCSVファイルをアップロードしてみます。
※アップロードのテストに使うだけですので、ファイルはCSVでなくても構いません。

ファイルを選択したら、「アップロード」をクリックします。

赤文字でメッセージが表示されたら、APIの呼び出しは成功です。

ファイルもしっかり「App_Data」フォルダに保存されています。

ビューからPOSTリクエストをおこない、Web APIにファイルをアップロードしてみました。
今回のサンプルではWeb APIの「App_Data」フォルダにアップロードしたファイルを保存していますが、
実際にはファイルを格納するサーバーやフォルダを用意して、そこに保存するのが良いと思います。
また、今回はなんとなく気分で「StreamContent」を使っていますが、「ByteArrayContent」を使っても実現可能です。