【问题标题】:Reading Limited Rows from Excel File Uploaded on IIS从 IIS 上上传的 Excel 文件中读取有限的行
【发布时间】:2016-06-06 07:05:02
【问题描述】:

我有一个托管在 IIS 上的 asp.net mvc 应用程序。我有一个表单,用户可以从中上传包含 50k+ 行的 excel 文件。我使用以下 C# 代码读取了 excel 文件。

    public DataTable GetExcelDataTable(string fileName)
    {
        string connectionString = Path.GetExtension(fileName) == "xls" ?
            string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data source={0}; Extended Properties=Excel 8.0;", fileName) :
            string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0}; Extended Properties=Excel 12.0;", fileName);

        var conn = new OleDbConnection(connectionString);
        using (var adapter = new OleDbDataAdapter("SELECT * FROM [Sheet1$]", conn))
        {
            var ds = new DataSet();

            adapter.Fill(ds);

            DataTable data = ds.Tables[0];

            conn.Close();
            conn.Dispose();
            adapter.Dispose();
            return data;
        }
    }

问题在于它最多只能读取 30k 行,但不能读取整个 excel 文件。

有趣的是,如果我使用 Visual Studio 运行 mvc 应用程序,我可以读取(使用相同的代码)所有行,但同样不能从 IIS(IIS 也在我的机器上)托管网站。

任何想法,为什么会发生这种情况?

【问题讨论】:

  • 读取 30k 条记录后出现任何错误?
  • 不,不。我没有收到任何错误。不管是 iis 还是 iisexpress(通过 vs)
  • 查看配置文件的样子可能会有所帮助,因为您的数据可能超出了读者配额。
  • 是的,我看过了。但是,我对 IIS 和 IIS Express 有相同的配置。它在 IIS Express 上运行,而不是在 IIS 上运行。
  • 检查这个link用户面临同样的问题

标签: c# asp.net-mvc excel iis oledb


【解决方案1】:

这种方法不需要在目标机器上安装excel

    NPOI.SS.UserModel.IWorkbook hssfworkbook;

    bool InitializeWorkbook(string path)
    {
        try
        {
            if (path.ToLower().EndsWith(".xlsx"))
            {
                FileStream file1 = File.OpenRead(path);
                hssfworkbook = new XSSFWorkbook(file1);
            }
            else
            {
                //read the template via FileStream, it is suggested to use FileAccess.Read to prevent file lock.
                //book1.xls is an Excel-2007-generated file, so some new unknown BIFF records are added. 
                using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read))
                {
                    hssfworkbook = new HSSFWorkbook(file);
                }
            }
            return true;
        }
        catch
        {
            return false;
        }
    }

如下:

public DataTable GetExcelDataTable(NPOI.SS.UserModel.IWorkbook hssfworkbook, int rowCount)
    {
        NPOI.SS.UserModel.ISheet sheet = hssfworkbook.GetSheetAt(0);
        System.Collections.IEnumerator rows = sheet.GetRowEnumerator();

        DataTable dt = new DataTable();

        bool skipReadingHeaderRow = rows.MoveNext();
        if (skipReadingHeaderRow)
        {
            dynamic row;
            if (rows.Current is NPOI.HSSF.UserModel.HSSFRow)
                row = (NPOI.HSSF.UserModel.HSSFRow)rows.Current;
            else
                row = (NPOI.XSSF.UserModel.XSSFRow)rows.Current;
            for (int i = 0; i < row.LastCellNum; i++)
            {
                ICell cell = row.GetCell(i);
                if (cell != null)
                {
                    dt.Columns.Add(cell.ToString());
                }
                else
                {
                    dt.Columns.Add(string.Empty);
                }
            }
        }
        int cnt = 0;
        while (rows.MoveNext() && cnt < rowCount)
        {
            cnt++;
            dynamic row;
            if (rows.Current is NPOI.HSSF.UserModel.HSSFRow)
                row = (NPOI.HSSF.UserModel.HSSFRow)rows.Current;
            else
                row = (XSSFRow)rows.Current;

            DataRow dr = dt.NewRow();

            for (int i = 0; i < row.LastCellNum; i++)
            {
                ICell cell = row.GetCell(i);

                if (cell == null)
                {
                    dr[i - 1] = null;
                }
                else if (i > 0)
                {

                    dr[i - 1] = cell.ToString();
                }
            }
            dt.Rows.Add(dr);
        }

        return dt;
    }

或:

     public DataTable GetExcelDataTable(NPOI.SS.UserModel.IWorkbook hssfworkbook, int rowCount)
    {

        NPOI.SS.UserModel.ISheet sheet = hssfworkbook.GetSheetAt(0);
        System.Collections.IEnumerator rows = sheet.GetRowEnumerator();
        DataTable dt = new DataTable();
        bool skipReadingHeaderRow = rows.MoveNext();
        if (skipReadingHeaderRow)
        {
            dynamic row;
            if (rows.Current is NPOI.HSSF.UserModel.HSSFRow)
                row = (NPOI.HSSF.UserModel.HSSFRow)rows.Current;
            else
                row = (NPOI.XSSF.UserModel.XSSFRow)rows.Current;
            for (int i = 0; i < row.LastCellNum; i++)
            {
                ICell cell = row.GetCell(i);
                if (cell != null)
                {
                    dt.Columns.Add(cell.ToString());
                }
                else
                {
                    dt.Columns.Add(string.Empty);
                }
            }
        }
        int cnt = 0;
        while (rows.MoveNext() && cnt < rowCount)
        {
            cnt++;
            dynamic row;
            if (rows.Current is NPOI.HSSF.UserModel.HSSFRow)
                row = (HSSFRow)rows.Current;
            else
                row = (XSSFRow)rows.Current;
            DataRow dr = dt.NewRow();

            for (int i = 0; i < row.LastCellNum; i++)
            {
                ICell cell = row.GetCell(i);

                if (cell == null && i > 0)
                {
                    dr[i - 1] = null;
                }
                else if (i > 0)
                {
                    switch (cell.CellType)
                    {
                        case CellType.Blank:
                            dr[i - 1] = "[null]";
                            break;
                        case CellType.Boolean:
                            dr[i - 1] = cell.BooleanCellValue;
                            break;
                        case CellType.Numeric:
                            dr[i - 1] = cell.ToString();
                            break;
                        case CellType.String:
                            dr[i - 1] = cell.StringCellValue;
                            break;
                        case CellType.Error:
                            dr[i - 1] = cell.ErrorCellValue;
                            break;
                        case CellType.Formula:
                        default:
                            dr[i - 1] = "=" + cell.CellFormula;
                            break;
                    }

                }
            }
            dt.Rows.Add(dr);
        }

        return dt;
    }

或:

public DataTable GetExcelDataTable(NPOI.SS.UserModel.IWorkbook hssfworkbook, int segment, int rowCount)
    {
        NPOI.SS.UserModel.ISheet sheet = hssfworkbook.GetSheetAt(0);
        System.Collections.IEnumerator rows = sheet.GetRowEnumerator();

        DataTable dt = new DataTable();

        bool skipReadingHeaderRow = rows.MoveNext();
        if (skipReadingHeaderRow)
        {
            dynamic row;
            if (rows.Current is NPOI.HSSF.UserModel.HSSFRow)
                row = (NPOI.HSSF.UserModel.HSSFRow)rows.Current;
            else
                row = (NPOI.XSSF.UserModel.XSSFRow)rows.Current;
            for (int i = 0; i < row.LastCellNum; i++)
            {
                ICell cell = row.GetCell(i);
                if (cell != null)
                {
                    dt.Columns.Add(cell.ToString());
                }
                else
                {
                    dt.Columns.Add(string.Empty);
                }
            }
        }
        for (int i = 0; i < (segment - 1)*rowCount; i++)
        {
            if (!rows.MoveNext()) break;
        }

        int cnt = 0;
        while (rows.MoveNext() && cnt < rowCount)
        {
            cnt++;
            dynamic row;
            if (rows.Current is NPOI.HSSF.UserModel.HSSFRow)
                row = (NPOI.HSSF.UserModel.HSSFRow) rows.Current;
            else
                row = (NPOI.XSSF.UserModel.XSSFRow) rows.Current;

            DataRow dr = dt.NewRow();

            for (int i = 0; i < row.LastCellNum; i++)
            {
                ICell cell = row.GetCell(i);

                if (cell == null)
                {
                    dr[i - 1] = null;
                }
                else if (i > 0)
                {
                    switch (cell.CellType)
                    {
                        case CellType.Blank:
                            dr[i - 1] = "[null]";
                            break;
                        case CellType.Boolean:
                            dr[i - 1] = cell.BooleanCellValue;
                            break;
                        case CellType.Numeric:
                            dr[i - 1] = cell.ToString();
                            break;
                        case CellType.String:
                            dr[i - 1] = cell.StringCellValue;
                            break;
                        case CellType.Error:
                            dr[i - 1] = cell.ErrorCellValue;
                            break;
                        case CellType.Formula:
                        default:
                            dr[i - 1] = "=" + cell.CellFormula;
                            break;
                    }
                }
            }
            dt.Rows.Add(dr);
        }
        return dt;
    }

【讨论】:

  • 其实,如果你想使用OleDB读取excel文件,你只需要安装免费的Microsoft JET数据驱动即可。
  • @MaxLambertini 每个问题都有这么多的解决方案,所以取决于我们根据不同的情况选择一个
【解决方案2】:

你能发布一些你的服务器的规格吗?它是基于虚拟机和云的吗?过去我曾成功使用:

  1. 库格拉:https://sourceforge.net/projects/koogra/

  2. 非营利组织:http://npoi.codeplex.com/

    要读取 .xls 文件,但如果您可以将文件限制为 .xlsx,我会使用 ClosedXML。我在 Azure 的强大 VM 上使用 ClosedXML 阅读了大量 50K+ 的大文件,没有问题。我有一种感觉,你正在撞到服务器上的用户空间墙。如果用户达到这样的百分比,他们就会达到使用配额并结束任务。

【讨论】:

  • 但是,服务器规格重要吗?因为它正在 VS 在 localhost 上执行 IISExpress 而不仅仅是 IIS(也是 localhost)
  • 当然,Windows Server 2003+ 上的 IIS 习惯于在用户开始耗尽资源时阻止用户。我最初的想法是它达到了 30k 标记并由于达到某种阈值而断开连接。真的只是一种预感。
【解决方案3】:

这个问题可以通过读取25K + 25k = 50K两部分的数据来解决。 您只需将选择查询更新为:

SELECT TOP 25000 * FROM [Sheet1$]

【讨论】:

  • 我不确定,但你必须使用类似 rowid 的东西
  • 确实不错的做法,您甚至可以启动两个线程,一个从顶部读取,另一个从底部读取并更快地完成它。一个具有最大行数 /2 的简单 for 循环就可以了。
  • @levi 我相信select bottom 25000 或类似的东西应该存在。
【解决方案4】:

我自己创建了一个小样本,使用 SELECT TOP 和 ORDER BY 然后你可以得到结果:

检查代码:

  public DataSet GetExcelDataTable(string fileName)
    {
        string connectionString = Path.GetExtension(fileName) == "xls" ?
            string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data source={0}; Extended Properties=Excel 8.0;", fileName) :
            string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0}; Extended Properties=Excel 12.0;", fileName);

        var conn = new OleDbConnection(connectionString);

        DataTable data = new DataTable();
        DataTable data2 = new DataTable();
        var ds = new DataSet();

        using (var adapter = new OleDbDataAdapter("SELECT TOP 25000 Name, Surname FROM [Sheet1$] ORDER BY Name asc", conn))
        {
            adapter.Fill(data);
        }

        using (var adapter = new OleDbDataAdapter("SELECT TOP 25000 Name, Surname FROM [Sheet1$] ORDER BY Name desc", conn))
        {
            adapter.Fill(data2);
        }

        if (data.Rows.Count > 0)ds.Tables.Add(data);
        if (data2.Rows.Count > 0) ds.Tables.Add(data2);

        return ds;
    }

【讨论】:

    【解决方案5】:

    您可以尝试通过逐块读取行块来填充数据表,而不是进行一次读取。

    这种方法的美妙之处在于,您不仅限于 50k 条记录,还可以适应数据表的实际基数。

    此代码适用于我的机器(Win10 X64,VS2010 Express):

        public DataTable GetExcelDataTable(string fileName)
        {
            string connectionString = Path.GetExtension(fileName) == "xls" ?
                string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data source={0}; Extended Properties=Excel 8.0;", fileName) :
                string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0}; Extended Properties=Excel 12.0;", fileName);
    
            var conn = new OleDbConnection(connectionString);
            using (var adapter = new OleDbDataAdapter("SELECT * FROM [Sheet1$]", conn))
            {
                var dt = new DataTable();
    
                int recordRead = 0;
                int recordCur = 0;     //starting point
                int recordStep = 6789; //records to read
    
                //here, we read **recordStep** records instead of reading 
                //all excel data
                do
                {
                    recordRead = adapter.Fill( recordCur, recordStep, dt);
                    recordCur += recordRead; //increment starting point accordingly
                } while (recordRead > 0);
    
    
                conn.Close();
                conn.Dispose();
                adapter.Dispose();
                return dt;
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2015-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多