ASP.NET Core MVC 上傳、匯入、匯出功能詳解

Jeffcky發表於2017-03-03

前言

本君已成夜貓子,本節我們來講講ASP.NET Core MVC中的上傳,這兩天在研究批量匯入功能,本節順便簡單搞搞匯入、匯出,等博主弄妥當了再來和大家一併分享。

.NET Core MVC上傳

首先我們來看看官網的上傳的例子,再然後進行擴充訓練,官網的表單是這樣的。

<form method="post" enctype="multipart/form-data" asp-controller="UploadFiles" asp-action="Index">
    <div class="form-group">
        <div class="col-md-10">
            <p>Upload one or more files using this form:</p>
            <input type="file" name="files" multiple />
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-10">
            <input type="submit" value="上傳" />
        </div>
    </div>
</form>

在ASP.NET Core MVC中接收上傳的檔案需要用 IFormFile 來接收,該介面定義如下:

public interface IFormFile
{
    string ContentType { get; }
    string ContentDisposition { get; }
    IHeaderDictionary Headers { get; }
    long Length { get; }
    string Name { get; }
    string FileName { get; }
    Stream OpenReadStream();
    void CopyTo(Stream target);
    Task CopyToAsync(Stream target, CancellationToken cancellationToken = null);
}

後臺控制器關於上傳的Action方法進行如下定義:

[HttpPost("UploadFiles")]
        public async Task<IActionResult> Post(List<IFormFile> files)
        {
            long size = files.Sum(f => f.Length);

            // full path to file in temp location
            var filePath = Path.GetTempFileName();

            foreach (var formFile in files)
            {
                if (formFile.Length > 0)
                {
                    using (var stream = new FileStream(filePath, FileMode.Create))
                    {
                        await formFile.CopyToAsync(stream);
                    }
                }
            }
            return Ok(new { count = files.Count, size, filePath });
        }

為了很清楚地上傳檔案所在目錄,我們將官網例子進行一下改造。

public IActionResult UploadFiles(List<IFormFile> files)
        {
            long size = 0;
            foreach (var file in files)
            {
                //var fileName = file.FileName;
                var fileName = ContentDispositionHeaderValue
                                .Parse(file.ContentDisposition)
                                .FileName
                                .Trim('"');
                fileName = hostingEnv.WebRootPath + $@"\{fileName}";
                size += file.Length;
                using (FileStream fs = System.IO.File.Create(fileName))
                {
                    file.CopyTo(fs);
                    fs.Flush();
                }
            }
            ViewBag.Message = $"{files.Count}個檔案 /{size}位元組上傳成功!";
            return View();
        }

如上通過注入  private IHostingEnvironment hostingEnv; 來獲取網站根目錄路徑。在前臺表單中請求action方法用渲染的方式,如下:

<form method="post" enctype="multipart/form-data" asp-controller="Upload" asp-action="UploadFiles">
</form>

當然別忘記新增TagHelper:

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

成功上傳我們顯示上傳位元組大小,如下:

上傳的檔案在網站根目錄下我們能夠看到,如下:

上述我們只是牛刀小試通過表單提交,接下來我們進行擴充通過Ajax來提交。我們將表單型別submit修改為button,如下:

<div class="row">
            <div class="form-group">
                <div class="col-md-10">
                    <p>使用表單上傳多個檔案</p>
                    <input type="file" id="files" name="files" multiple />
                    @ViewBag.Message
                </div>
            </div>
        </div>
        <div class="row">
            <div class="form-group">
                <div class="col-md-10">
                    <input type="button"  id="upload" class="btn btn-success" style="cursor:pointer;width:100px;" value="上傳" />
                </div>
            </div>
        </div>

我們通過FormData物件來獲取檔案從而進行Ajax提交,如下:

$(function () {
            $("#upload").click(function (evt) {
                var fileUpload = $("#files").get(0);
                var files = fileUpload.files;
                var data = new FormData();
                for (var i = 0; i < files.length ; i++) {
                    data.append(files[i].name, files[i]);
                }
                $.ajax({
                    type: "POST",
                    url: "/Upload/UploadFiles",
                    contentType: false,
                    processData: false,
                    data: data,
                    success: function (message) {
                        alert(message);
                    },
                    error: function () {
                        alert("上傳檔案出現錯誤!");
                    }
                });
            });
        });

此時後臺則需要進行略微修改,我們不再需要IFormFile介面來獲取檔案,通過請求中的表單獲取,如下:

public IActionResult UploadFiles()
        {
            long size = 0;
            var files = Request.Form.Files;
            foreach (var file in files)
            {
                //var fileName = file.FileName;
                var fileName = ContentDispositionHeaderValue
                                .Parse(file.ContentDisposition)
                                .FileName
                                .Trim('"');
                fileName = hostingEnv.WebRootPath + $@"\{fileName}";
                size += file.Length;
                using (FileStream fs = System.IO.File.Create(fileName))
                {
                    file.CopyTo(fs);
                    fs.Flush();
                }
            }
            ViewBag.Message = $"{files.Count}個檔案 /{size}位元組上傳成功!";
            return View();
        }

到這裡關於ASP.NET Core MVC中的上傳就告一段落,還是比較簡單但是算是比較常見的需求。

匯入、匯出Excel

專案中需要用到批量匯入和匯出於是進行了一點研究,.net core剛出世時還未有對於.net core中Excel的匯出,但是見過園中有熱心園友分享並製作了.net core中匯出Excel,但是博主發現在2月19號有老外已針對.net core的Excel匯出和匯入目前版本為1.3基於EPPlus,功能和EPPlus差不多,不過是移植到了.net core中,下面我們一起來看看。首先我們下載EPPlus.Core程式包,如下:

我們直接上匯出程式碼:

[HttpGet]
        [Route("Export")]
        public string Export()
        {
            string sWebRootFolder = _hostingEnvironment.WebRootPath;
            string sFileName = @"Jeffcky.xlsx";
            string URL = string.Format("{0}://{1}/{2}", Request.Scheme, Request.Host, sFileName);
            FileInfo file = new FileInfo(Path.Combine(sWebRootFolder, sFileName));
            if (file.Exists)
            {
                file.Delete();
                file = new FileInfo(Path.Combine(sWebRootFolder, sFileName));
            }
            using (ExcelPackage package = new ExcelPackage(file))
            {
                // add a new worksheet
                ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Jeffcky");

                //sheet header
                worksheet.Cells[1, 1].Value = "ID";
                worksheet.Cells[1, 2].Value = "Name";
                worksheet.Cells[1, 3].Value = "Age";

                //Add values
                worksheet.Cells["A2"].Value = 1000;
                worksheet.Cells["B2"].Value = "Jeffcky1";
                worksheet.Cells["C2"].Value = 18;

                worksheet.Cells["A3"].Value = 1001;
                worksheet.Cells["B3"].Value = "Jeffcky2";
                worksheet.Cells["C3"].Value = 19;

                package.Save(); //Save the workbook.
            }
            return URL;

        }

這裡我們進行統一封裝下來進行匯出只需要設定匯出屬性和列表資料即可,如下:

public IActionResult Export()
        {
            var properties = new PropertyByName<Person>[]
            {
                new PropertyByName<Person>("Id",d=>d.Id),
                new PropertyByName<Person>("Name",d=>d.Name),
                new PropertyByName<Person>("Age",d=>d.Age)
            };

            var list = new List<Person>()
            {
                new Person() {Id=1,Name="Jeffcky1",Age=18 },
                new Person() {Id=2,Name="Jeffcky2",Age=19 },
                new Person() {Id=3,Name="Jeffcky3",Age=20 },
                new Person() {Id=4,Name="Jeffcky4",Age=21 },
                new Person() {Id=5,Name="Jeffcky5",Age=22 }
            };
            var bytes = _ExportManager.ExportToXlsx<Person>(properties, list);
            return new FileContentResult(bytes, MimeTypes.TextXlsx);
        }

說完匯出我們再來看匯入,我們來讀取剛剛匯入的資料返回到頁面上:

public string Import()
        {
            string sWebRootFolder = _hostingEnvironment.WebRootPath;
            string sFileName = @"Jeffcky.xlsx";
            FileInfo file = new FileInfo(Path.Combine(sWebRootFolder, sFileName));
            try
            {
                using (ExcelPackage package = new ExcelPackage(file))
                {
                    StringBuilder sb = new StringBuilder();
                    ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
                    int rowCount = worksheet.Dimension.Rows;
                    int ColCount = worksheet.Dimension.Columns;
                    bool bHeaderRow = true;
                    for (int row = 1; row <= rowCount; row++)
                    {
                        for (int col = 1; col <= ColCount; col++)
                        {
                            if (bHeaderRow)
                            {
                                sb.Append(worksheet.Cells[row, col].Value.ToString() + "\t");
                            }
                            else
                            {
                                sb.Append(worksheet.Cells[row, col].Value.ToString() + "\t");
                            }
                        }
                        sb.Append(Environment.NewLine);
                    }
                    return sb.ToString();
                }
            }
            catch (Exception ex)
            {
                return "Some error occured while importing." + ex.Message;
            }
        }

此時我們再來對匯入進行統一封裝下,如下:

[HttpGet]
        [Route("Import")]
        public void Import()
        {
            string sWebRootFolder = _hostingEnvironment.WebRootPath;
            string sFileName = @"Jeffcky.xlsx";
            FileStream fs = new FileStream(Path.Combine(sWebRootFolder, sFileName), FileMode.Open, FileAccess.Read, FileShare.Read);
            var list = _ImportManager.ImportPersonFromXlsx(fs);
        }

匯入大概就介紹完畢了,要我說真正的難點不在於利用EPPlus匯入和匯出,難點在於批量匯入,批量進行匯入後對資料格式的檢驗,如果給定一個匯入模板,然後再匯入批量資料怎麼確保使用者給的資料格式完全是正確的以及資料沒有重複的校驗,這兩天基本上是完成了批量的匯入,大概分為:資料必填項的校驗、資料格式的校驗、資料庫是否存在資料的校驗、資料匯入部分匯入失敗返回格式的使用者體驗。當利用NPOI、EPPlus來匯入和匯出這樣的功能再簡單不過了,但是如果遇到了不同的場景怎麼讓使用者體驗更好的使用這是一個問題,如果資料匯入失敗我們怎麼去提示使用者呢,還有如果Excel中有下拉框和合並的單元格資料我們怎麼去獲取這又是一個問題,可能很多簡歷上寫著會利用NPOI和EPPlus的匯入和匯出,其實沒什麼看頭,二者不過是一個工具罷了,如何利用工具去應用到複雜的場景並舉例那才算是高階的東西。

總結

本節我們稍微介紹了.net core中的下載、匯入和匯出,如果有可能的話後續會給出關於EPPlus中高階的知識,比如如上提出的獲取合併列資料還有獲取圖片等等,我們下節再會,哦,關於SQL Server有時間會定期進行更新,see u。

相關文章