ASP.NET MVC、C#でファイルをインポートするとき

#はじめに
ASP.NET MVC、C#でのファイルをインポートについてのメモです。
ここでのインポートはファイルの内容を、アプリケーション内にデータを取り込むことを指します。
間違いや勘違いに関しましてはご指摘いただけると幸いです。

##インポートの流れ

この記事におけるインポートの流れは以下を想定しています。
1.ユーザーが画面からファイルを選択しSubmitボタンを押下する。
2.ファイルをサーバー上に保存
3.保存したファイルを読み込み、要素を取り出す。
4.保存したファイルの破棄
5.取り出した要素を何らかの方法で保存する（DBに登録する等）

5の処理はファイルであることで大きく変わることではないので、1から4までの内容とします。

##画面

``` csharp:Upload.cshtml

@model TestProject.Models.Test.UploadModels
@using (Html.BeginForm("Upload", "Test", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()
    <div>
        @Html.LabelFor(m => m.UploadFile)
    </div>
    <div>
        @Html.TextBoxFor(m => m.UploadFile, new {@type= "file"})
        @Html.ValidationMessageFor(m => m.UploadFile)
    </div>
    <div>
        <input type="submit" value="送信する" />
    </div>
```
これを動かすと以下のような画面になります。

![2018-04-17.png](https://qiita-image-store.s3.amazonaws.com/0/247226/0e6a6315-fb3e-45f9-609c-9cc82679f43c.png)


まず、ファイルを送信するためにフォームに```new { enctype = "multipart/form-data" }```を指定してください。

``` csharp:
@Html.TextBoxFor(m => m.UploadFile, new {@type= "file"})
```

上記で```<input type="file">```が生成され、ファイルの参照ボタン、ファイル名を表示する場所が作られます。ファイルを選択してらすぐにサーバーにファイルが送信されるわけではなく、Submitボタンを押下される必要があります。

##モデル

Submitボタン押下後は、Viewで宣言しているUploadModelsの、UploadFileプロパティにファイルの情報が格納されます。
この際、独自の検証属性を付けることも可能のようです。

``` csharp:UploadModels.cs
    public class UploadModels
    {
        [DisplayName("ファイル入力")]
        public HttpPostedFileBase UploadFile { get; set; }
    }
```


##サーバー側での処理

``` csharp:Test.cs
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Upload(UploadModels model)
{
    string fileName = string.Empty;
    string path = string.Empty;
    List<string> list = new List<string>();
    if (ModelState.IsValid)
    {
        try
        {
            //その1
            HttpPostedFileBase file = model.UploadFile;
            string fileName = Path.GetFileName(file.FileName);

            //その2
            string path = System.Web.Hosting.HostingEnvironment.MapPath(""C:/test/test/" + fileName);
            model.FilePath = path;
            file.SaveAs(path);

            //その3
            //ファイルを読み込む
        }
        finally
        {
            System.IO.File.Delete(path);
        }
        
        //取得したデータをDBに登録する処理

        }
    return View();
    }
```

上記で行いました。
その1、その2、その3の3つに分類しています。

###その1

``` csharp:

HttpPostedFileBase file = model.UploadFile;
string fileName = Path.GetFileName(file.FileName);

```
上記では、モデルに格納したUploadFileを取り出し、ファイル名を取得しています。

###その2
``` csharp:

string path = System.Web.Hosting.HostingEnvironment.MapPath(
                "C:/test/test/" + fileName);
file.SaveAs(path);

```

上記でパスを指定し、そこに選択したファイルをサーバーに保存しています。
これはファイルを読み込むための一時的な保存です。

``` csharp
finally
{
    System.IO.File.Delete(path);
}
```
読み込み終了後、finallyで削除しています。

###その3

保存したファイルのパスから、ファイルを読み込みます。ファイルの読み込む方法はファイルの種類によって使用するクラスが変わります。
ここではExcelファイルとCSVファイルの読み込むコードを記載いたします。

####CSV

``` csharp
 using (var reader = new System.IO.StreamReader(path, System.Text.Encoding.GetEncoding("Shift_JIS")))
{
    // データを行単位で取得
    while (!reader.EndOfStream)
    {
        string[] readArray = reader.ReadLine().Split(',');
        Console.WriteLine(read);
        foreach(var read in readArray)
        {
            Console.WriteLine(read);
        }
        Console.WriteLine("----------");
    }
    reader.Close();
}
```

まずCSVを読み込む方法ですが、StreamReaderを使用して読み込むことができます。
文字コードの指定がない場合、文字化けが起こることがありますのでそこだけ注意が必要と思います。
ここでは1行ずつ読み込みこんでいます。

####Excel

``` csharp
using (XLWorkbook book = new XLWorkbook(path, XLEventTracking.Disabled))
{
    IXLWorksheet sheet = book.Worksheet(1);

    IXLTable table = sheet.RangeUsed().AsTable();

    // データを行単位で取得
    foreach (var row in table.DataRange.Rows()){
        var cellList = row.Cells().ToList();
        Console.WriteLine(read);
        foreach(var cell in cellList)
        {
            Console.WriteLine(read);
        }
            Console.WriteLine("----------");
    }
}
```

次にExcelを読み込む方法ですが、ここではColsedXmlを使用しています。

``` csharp
IXLWorksheet sheet = book.Worksheet(1);
IXLTable table = sheet.RangeUsed().AsTable();
```

上記で読み込むシートを指定し、シートの使用している範囲を取得しています。

``` csharp
table.DataRange.Rows()
```

使用している範囲を1行ずつ取り出しています。

#終わりに

インポートから読み込みまで一連の処理として個々の処理は浅く触れる程度にしか書けていないため、今後もう少し掘り下げた記事を書きたいです。