NPOI导入Excel实战教程(Vue+C#)

背景

        使用情景介绍:前端使用Vue,后端语言使用C#。

一、前端

1、api

首先,我们需要在Vue项目中,定义一个api接口:

url指向x导入Excel的后端接口路由,

请求参数为post,

文本类型为“multipart/form-data”

具体格式参考下边代码。

export function ImportTest(data) {
  return request({
    url: '/api/leeway/ImportTest',
    method: "post",
    ContentType: "multipart/form-data",
    data: data
  });
}

2、上传文件对话框

定义一个上传文件的对话框(dialog)

把对话框代码放进需要导入文件的页面上,并写一个按钮绑定点击事件,实现点击触发打开对话框功能。

这里我用的是importTest()函数,点击实现将对话框属性importTestDialogVisible置为true。

代码放在下边第三点(相关调用方法)

 <el-dialog
      title="导入测试(请按照测试模板导入)"
      :visible.sync="importTestDialogVisible"
      :modal-append-to-body="false"
      :append-to-body="true"
      top="7vh"
      align="center"
    >
      <div>
        <el-upload
          drag
          :limit="limitNum"
          :auto-upload="false"
          accept=".xlsx"
          :action="UploadUrl()"
          :before-upload="beforeUploadFile"
          :on-change="fileChange"
          :on-exceed="exceedFile"
          :on-success="handleSuccess"
          :on-error="handleError"
          :file-list="fileList"
          :on-remove="removefile"
        >
          <i class="el-icon-upload" />
          <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
          <div slot="tip" class="el-upload__tip">
            只能上传xlsx文件,且不超过10M
          </div>
        </el-upload>
        <br>
        <div style="margin-bottom: 10px">
          <el-button icon="el-icon-download" size="small" type="primary" @click="exportTestTemplate">测试模板</el-button>
          <el-button icon="el-icon-upload2" size="small" type="primary" @click="uploadFile">立即上传</el-button>
          <el-button size="small" @click="canceluploadFile">取消</el-button>
        </div>
      </div>
    </el-dialog>

 3、相关数据定义和调用方法

这里提供给与本次实战的上传文件对话框相关的函数,另外还有导出模板函数exportTestTemplate

我们做一个导出Excel模板功能,只需两个步骤:

1、在本地新创建一个excel文件并把它命名为“导入测试模板”。

2、把该模板文件放在Vue项目的public文件夹下。

//导出模板调用方法,一般把模板放在项目的public目录下
exportTestTemplate(){
      const aTag = document.createElement("a");
      aTag.href = "./导入测试模板.xlsx";  //这里是放模板的地方
      aTag.download = "测试模板.xlsx";
      aTag.click();
    },    
importTest(){
      this.importTestDialogVisible = true;
    },
    removefile() {
      this.fileList = [];
    },
    canceluploadFile() {
      this.importQuotaDialogVisible = false;
    },
    //上传文件调用方法
    uploadFile(){
      if (this.fileList.length === 0) {
        this.$message.warning("请先选择文件");
      } else {
        const formData = new FormData();
        formData.append("file", this.fileList[0]);
        console.log(formData);
        console.log(this.fileList);
        //ImportTest对应api里的方法名
        ImportTest(formData).then((res) => {
          if (res.code == "0") {
            this.$message.success("导入测试成功!");
          } else {
            this.$message.error(res.message);
          }
          this.importTestDialogVisible = false;
          this.fileList = [];
          //这里可以重新调用一边父页面的加载方法,可忽略
        });
      }
    },
    // 文件超出个数限制时的钩子
    exceedFile(files, fileList) {
      this.$message.warning(
        `只能选择 ${this.limitNum} 个文件,当前共选择了 ${
          files.length + fileList.length
        } 个`
      );
    },
    // 文件状态改变时的钩子
    fileChange(file, fileList) {
      console.log(file.raw);
      this.fileList.push(file.raw);
      console.log(this.fileList);
    },
    // 上传文件之前的钩子, 参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传
    beforeUploadFile(file) {
      console.log("before upload");
      console.log(file);
      const extension = file.name.substring(file.name.lastIndexOf(".") + 1);
      const size = file.size / 1024 / 1024;
      if (extension !== "xlsx") {
        this.$message.warning("只能上传后缀是.xlsx的文件");
      }
      if (size > 10) {
        this.$message.warning("文件大小不得超过10M");
      }
    },
    // 文件上传成功时的钩子
    handleSuccess(res, file, fileList) {
      this.$message.success("文件上传成功!");
    },
    // 文件上传失败时的钩子
    handleError(err, file, fileList) {
      if (err) {
        console.log(err);
      }
      this.$message.error("文件上传失败");
    },
    UploadUrl: function() {
      // 因为action参数是必填项,我们使用二次确认进行文件上传时,直接填上传文件的url会因为没有参数导致api报404,所以这里将action设置为一个返回为空的方法就行,避免抛错
      return "";
    },

二、后端

1、简单粗暴写法

需要接收方式、路由,这里的route内容与第一部分api里的url相对应。

[HttpPost]
[Route("ImportTest")]
public async Task<ServiceResult> ImportTest([FromForm] IFormFile file)
{
    var result = new ServiceResult<ImportTestDto>();
    try
    {
        string webRootPath = _hostingEnvironment.ContentRootPath;
        //定义存放上传文件的路径,需自己创建好文件夹
        string filePath = webRootPath + $"/FileUpload/TestFiles/";

        if (!Directory.Exists(filePath))
        {
            Directory.CreateDirectory(filePath);
        }
        var formFile = file;
        if (formFile.Length > 0)
        {
            string fileExt = formFile.FileName.Split('.')[1]; //文件扩展名,不含“.”
            long fileSize = formFile.Length; //获得文件大小,以字节为单位
            string newFileName = Guid.NewGuid() + "." + fileExt; //随机生成新的文件名

            string fileNamePath = Path.Combine(filePath, newFileName);
            using (var stream = new FileStream(fileNamePath, FileMode.Create))
            {
                formFile.CopyTo(stream);
                stream.Flush();
            }
            List<ImportTestDto> ListEntity = new List<ImportTestDto>();
            //调用ExcelHelper方法
            DataTable dt = NPOIHelper.ExcelToDataTable(fileNamePath, true);
            if (dt.Rows.Count > 0)
            {
                int rowStart = 2;
                string msg = string.Empty;
                foreach (DataRow dr in dt.Rows)
                {
                    var oneMsg = $"【数据校验】第{rowStart}行:";
                    ImportTestDto model = new ImportTestDto();

                    if (dr["姓名"].ToString() == "")
                    {
                        msg += "姓名为空<br/>";
                    }
                    else
                    {
                        model.Name = dr["姓名"].ToString();
                    }
                    if (dr["号码"].ToString() == "")
                    {
                        msg += "姓名为空<br/>";
                    }
                    else if (dr["号码"].ToString().Length != 11)
                    {
                        msg += "号码长度不是11位<br/>";
                    }
                    else
                    {
                        model.Number = dr["号码"].ToString();
                    }

                    if (ListEntity.Where(u => u.StudentNo == model.StudentNo).Count() > 0)
                    {
                        msg += "与Excel前面行的学号重复<br/>";
                    }
                    //校验系统是否已有导入过该学号,此方法需要自己写
                    if (await _studentService.IsExistStudent(model.StudentNo))
                    {
                        msg += "学号在系统中已存在,请确认<br/>";
                    }


                    if (dr["金额"].ToString() == "")
                    {
                        msg += "金额为空<br/>";
                    }
                    else if (decimal.TryParse(dr["金额"].ToString(), out decimal amount))
                    {
                        if (amount < 0)
                        {
                            msg += "金额不能为负数<br/>";
                        }
                        else
                        {
                            model.Amount = amount;
                        }
                    }
                    else
                    {
                        msg += "金额格式有误<br/>";
                    }
                    if (dr["日期"].ToString() == "")
                    {
                        msg += "日期为空<br/>";
                    }
                    else if (DateTime.TryParse(dr["日期"].ToString(), out DateTime invoiceDate))
                    {
                        model.Date = date;
                    }
                    else
                    {
                        msg += "日期格式有误<br/>";
                    }
                    if (oneMsg != $"【数据校验】第{rowStart}行:")
                    {
                        msg += $"{oneMsg}";
                    }
                    rowStart++;
                    ListEntity.Add(model);
                }
                if (msg != string.Empty)
                {
                    result.IsFailed(msg);
                }
                else
                {
                    if (ListEntity.Count > 0)
                    {
                        //InsertStudents为插入数据列表的方法,需要自己写
                        var SaveResult = await _studentService。InsertStudents(ListEntity);
                        if (SaveResult)
                        {
                            result.IsSuccess("导入成功");
                        }
                        else
                        {
                            result.IsFailed("导入失败");
                        }

                    }
                    else
                    {
                        result.IsFailed("导入失败,没有导入数据!");
                    }
                }
            }
            else
            {
                result.IsFailed("导入失败,没有数据!");
            }
        }
    }
    catch (Exception ex)
    {
        result.IsFailed(ex.Message);
    }
    return result;
}

2、改进写法

在这,使用了反射做了一个小的优化,感兴趣的可以看看

[HttpPost]
[Route("ImportTest")]
public async Task<ServiceResult> ImportTest([FromForm] IFormFile file)
{
    try
    {
        string webRootPath = _hostingEnvironment.ContentRootPath;
        //定义存放上传文件的路径,需自己创建好文件夹
        string filePath = webRootPath + $"/FileUpload/TestFiles/";

        if (!Directory.Exists(filePath))
        {
            Directory.CreateDirectory(filePath);
        }
        var formFile = file;
        if (formFile.Length > 0)
        {
            string fileExt = formFile.FileName.Split('.')[1]; //文件扩展名,不含“.”
            long fileSize = formFile.Length; //获得文件大小,以字节为单位
            string newFileName = Guid.NewGuid() + "." + fileExt; //随机生成新的文件名

            string fileNamePath = Path.Combine(filePath, newFileName);
            using (var stream = new FileStream(fileNamePath, FileMode.Create))
            {
                formFile.CopyTo(stream);
                stream.Flush();
            }

            List<ImportTestDto> ListEntity = new List<ImportTestDto>();
            //调用ExcelHelper方法, 后边会给出该类的代码
            DataTable dt = NPOIHelper.ExcelToDataTable(fileNamePath, true);
            #region 导入模板对应的字段配置(这里也可以使用反射,获取dto的中文注释,这么写是防止注释被改导致导入Excel出错)
            var dataLabel = new Dictionary<string, string>
                    {
                        { "Jan", "1月" },
                        { "Feb", "2月" },
                        { "Mar", "3月" },
                        { "Apr", "4月" },
                        { "May", "5月" },
                        { "Jun", "6月" },
                        { "Jul", "7月" },
                        { "Aug", "8月" },
                        { "Sep", "9月" },
                        { "Oct", "10月" },
                        { "Nov", "11月" },
                        { "Dec", "12月" },
                        { "Name", "姓名" },
                        { "Scale", "比例(%)" },
                        { "StudentNo", "学号" }
                    };
            #endregion

            if (dt.Rows.Count > 0)
            {
                int rowStart = 2;
                string msg = string.Empty;
                PropertyInfo[] propertyInfos = typeof(ImportTestDto).GetProperties();
                foreach (DataRow dr in dt.Rows)
                {
                    var oneMsg = $"【数据校验】第{rowStart}行:";
                    ImportTestDto model = new ImportTestDto();
                    foreach (var item in dataLabel)
                    {
                        //全部列统一进行空校验
                        if (dr[$"{item.Value}"].ToString() == "")
                        {
                            oneMsg += $"{item.Value}数据为空<br/>";

                        }
                        else
                        {
                            //对特殊列格式进行校验
                            if (item.Value.Contains("月"))
                            {
                                if (decimal.TryParse(dr[$"{item.Value}"].ToString(), out decimal amount))
                                {
                                    if (amount < 0)
                                    {
                                        oneMsg += $"{item.Value}不能为负数<br/>";
                                    }
                                    else
                                    {
                                        propertyInfos.First(p => p.Name == item.Key).SetValue(model, amount);
                                    }
                                }
                                else
                                {
                                    oneMsg += $"{item.Value}格式有误<br/>";
                                }
                            }
                            else if (item.Value.Contains("%"))
                            {
                                decimal.TryParse(dr[$"{item.Value}"].ToString(), out decimal scaleLabel);
                                propertyInfos.First(p => p.Name == item.Key).SetValue(model, Convert.ToInt32(scaleLabel * 100).ToString());
                            }
                            else
                            {
                                if (ListEntity.Where(u => u.StudentNo== dr[$"{item.Value}"].ToString()).Count() > 0)
                                {
                                    oneMsg += $"{item.Value}与Excel前面行的StudentNo重复<br/>";
                                }
                                propertyInfos.First(p => p.Name == item.Key).SetValue(model, dr[$"{item.Value}"].ToString());
                            }
                        }
                    }
                    if (oneMsg != $"【数据校验】第{rowStart}行:")
                    {
                        msg += $"{oneMsg}";
                    }
                    rowStart++;
                    ListEntity.Add(model);
                }
                if (msg != string.Empty)
                {
                    result.IsFailed(msg);
                }
                else
                {
                    if (ListEntity.Count > 0)
                    {
                        //InsertStudents为插入数据列表的方法,需要自己写
                        var SaveResult = await _studentService。InsertStudents(ListEntity);
                        if (SaveResult)
                        {
                            result.IsSuccess("导入成功");
                        }
                        else
                        {
                            result.IsFailed("导入失败");
                        }

                    }
                    else
                    {
                        result.IsFailed("导入失败,没有导入数据!");
                    }
                }
            }
            else
            {
                result.IsFailed("导入失败,没有数据!");
            }

        }
    }
    catch (Exception ex)
    {
        result.IsFailed(ex.Message);
    }
    return result;
}

三、NPOI类

在把文件转成Datatabel时,需用到一下方法,代码如下所示:

public class NPOIHelper
{

    /// <summary>  
    /// 将excel导入到datatable  
    /// </summary>  
    /// <param name="filePath">excel路径</param>  
    /// <param name="isColumnName">第一行是否是列名</param>  
    /// <returns>返回datatable</returns>  
    public static DataTable ExcelToDataTable(string filePath, bool isColumnName)
    {
        DataTable dataTable = null;
        FileStream fs = null;
        DataColumn column = null;
        DataRow dataRow = null;
        IWorkbook workbook = null;
        ISheet sheet = null;
        IRow row = null;
        ICell cell = null;
        int startRow = 0;
        try
        {
            using (fs = System.IO.File.OpenRead(filePath))
            {
                // 2007版本  
                if (filePath.IndexOf(".xlsx") > 0)
                    workbook = new XSSFWorkbook(fs);
                // 2003版本  
                else if (filePath.IndexOf(".xls") > 0)
                    workbook = new HSSFWorkbook(fs);

                if (workbook != null)
                {
                    sheet = workbook.GetSheetAt(0);//读取第一个sheet,当然也可以循环读取每个sheet  
                    dataTable = new DataTable();
                    if (sheet != null)
                    {
                        int rowCount = sheet.LastRowNum;//总行数  
                        if (rowCount > 0)
                        {
                            IRow firstRow = sheet.GetRow(0);//第一行  
                            int cellCount = firstRow.LastCellNum;//列数  

                            //构建datatable的列  
                            if (isColumnName)
                            {
                                startRow = 1;//如果第一行是列名,则从第二行开始读取  
                                for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
                                {
                                    cell = firstRow.GetCell(i);
                                    if (cell != null)
                                    {
                                        if (cell.StringCellValue != null)
                                        {
                                            column = new DataColumn(cell.StringCellValue);
                                            dataTable.Columns.Add(column);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
                                {
                                    column = new DataColumn("column" + (i + 1));
                                    dataTable.Columns.Add(column);
                                }
                            }

                            //填充行  
                            for (int i = startRow; i <= rowCount; ++i)
                            {
                                row = sheet.GetRow(i);
                                if (row == null) continue;
                                if (row.FirstCellNum == -1) continue;
                                dataRow = dataTable.NewRow();
                                for (int j = row.FirstCellNum; j < cellCount; ++j)
                                {
                                    cell = row.GetCell(j);
                                    if (cell == null)
                                    {
                                        dataRow[j] = "";
                                    }
                                    else
                                    {
                                        //CellType(Unknown = -1,Numeric = 0,String = 1,Formula = 2,Blank = 3,Boolean = 4,Error = 5,)  
                                        switch (cell.CellType)
                                        {
                                            case CellType.Blank:
                                                dataRow[j] = "";
                                                break;
                                            case CellType.Numeric:
                                                short format = cell.CellStyle.DataFormat;

                                                //对时间格式(2015.12.5、2015 / 12 / 5、2015 - 12 - 5等)的处理
                                                //  if (format == 14 || format == 31 || format == 57 || format == 58 || format == 176 || format == 177
                                                if (HSSFDateUtil.IsCellDateFormatted(cell))
                                                {
                                                    dataRow[j] = cell.DateCellValue;
                                                }
                                                else { dataRow[j] = cell.NumericCellValue; }

                                                break;
                                            case CellType.String:
                                                dataRow[j] = cell.StringCellValue;
                                                break;
                                        }
                                    }
                                }
                                dataTable.Rows.Add(dataRow);
                            }
                        }
                    }
                }
            }
            return dataTable;
        }
        catch (Exception ex)
        {
            if (fs != null)
            {
                fs.Close();
            }
            throw ex;
        }
    }
}

四、致谢

        感谢大家的阅读,祝大家学习愉快,记得点赞关注支持一波!

Supongo que te gusta

Origin blog.csdn.net/Leewayah/article/details/131581487
Recomendado
Clasificación