2013-03-12 42 views
0

我的應用程序具有一項功能,可以從中導出數據庫中的數據。它使用Ionic ZIP庫構建一個包含數據的zip文件。構建Zip文件時減少內存使用量

該過程通過檢索每個1,000行的頁面中的數據來工作。檢索的數據包含每行數據2個圖像(JPEGS)。 「導出」由HTML文件和HTML表格組成。該表包含每行的重要信息和圖像的鏈接。每個圖像都以JPEG文件形式存儲在ZIP文件中。寫入的HTML包含一個鏈接到適當圖像文件的錨標記。

一切正常,但我最近發現導出大量行時發生OutOfMemoryException(在發現問題的情況下超過24,000)。在看我的代碼,我沒有看到任何明顯的滲漏(否則我不會被張貼這一點,我會;?))

這裏是正在導出的數據輸出的方法:

private void OutputReads(StreamWriter writer, ZipFile zipFile, BackgroundWorker worker, ExportArgs args) { 
    int ReadCount = Math.Min(args.ReadMatches, args.ResultsLimit); 

    writer.WriteLine("<h2>Matching Reads:</h2>"); 
    writer.WriteLine("<p>Number in Database: {0}</p>", args.ReadMatches.ToString("#,##0")); 
    writer.WriteLine("<p>Number in Report: {0}</p>",  ReadCount .ToString("#,##0")); 
    writer.WriteLine(); 

    if (args.ReadMatches == 0) { 
     return; 
    } 

    writer.WriteLine("<table border=\"1\" cellspacing=\"0\" bordercolordark=\"black\" bordercolorlight=\"black\">"); 
    writer.WriteLine("<tr>"); 
    writer.WriteLine("<th width=\"110\"><b>Plate</b></th>"); 
    writer.WriteLine("<th width=\"60\"><b>State</b></th>"); 
    writer.WriteLine("<th width=\"200\"><b>Date/Time</b></th>"); 
    writer.WriteLine("<th width=\"142\"><b>Latitude</b?</th> "); 
    writer.WriteLine("<th width=\"142\"><b>Longitude</b?</th> "); 
    writer.WriteLine("<th width=\"125\"><b>Alarm Class</b></th>"); 
    writer.WriteLine("<th width=\"150\"><b>Notes</b></th>"); 
    writer.WriteLine("<th width=\"150\"><b>Officer Notes</b></th>"); 
    writer.WriteLine("<th width=\"150\"><b>Images</b></th>"); 
    writer.WriteLine("</tr>"); 

    int noPages = ReadCount/args.PageSize + (ReadCount % args.PageSize == 0 ? 0 : 1); 

    for (int pageNo = 0, count = 0; pageNo < noPages; pageNo++) { 
     if (worker.CancellationPending) { 
      return; 
     } 

     ReadViewModel[] reads = null; 

     try { 
      reads = DataInterface.GetReads(args.LocaleCode, args.Plate, 
              args.StartDate, args.EndDate, 
              args.AlarmClasses, args.HotListId, 
              args.PageSize, pageNo 
             ); 

     } catch (DataAccessException ex) { 
      . . . 
     } catch (ThreadAbortException) { 
      // The thread is stopping. Stop processing now. 

     } catch (Exception ex) { 
      . . . 
     } 

     foreach (ReadViewModel read in reads) { 
      if (worker.CancellationPending) { 
       return; 
      } 

      writer.WriteLine("<tr>"); 
      writer.WriteLine("<td width=\"110\"><p align=\"left\" style=\"text-align:left\">{0}</p></td>" , read.Plate); 
      writer.WriteLine("<td width=\"60\" ><p align=\"center\" style=\"text-align:center\">{0}</p></td>", read.State); 
      writer.WriteLine("<td width=\"150\"><p align=\"center\" style=\"text-align:center\">{0}</p></td>", read.TimeStamp); 
      writer.WriteLine("<td width=\"125\"><p align=\"center\" style=\"text-align:center\">{0}</p></td>", read.GPSInformation == null ? "&nbsp;" : read.GPSInformation.Position.Latitude.ToString( "##0.000000")); 
      writer.WriteLine("<td width=\"125\"><p align=\"center\" style=\"text-align:center\">{0}</p></td>", read.GPSInformation == null ? "&nbsp;" : read.GPSInformation.Position.Longitude.ToString("##0.000000")); 
      writer.WriteLine("<td width=\"125\"><p align=\"center\" style=\"text-align:center\">{0}</p></td>", read.AlarmClass  == null ? "&nbsp;" : read.AlarmClass); 
      writer.WriteLine("<td width=\"150\"><p align=\"left\" style=\"text-align:left\">{0}</p></td>" , read.  Notes == null ? "&nbsp;" : read.  Notes); 
      writer.WriteLine("<td width=\"150\"><p align=\"left\" style=\"text-align:left\">{0}</p></td>" , read.OfficerNotes == null ? "&nbsp;" : read.OfficerNotes); 

      if (read.ImageData == null) { 
       try { 
        DataInterface.GetPlateImage(read); 
       } catch (DataAccessException ex) { 
        . . . 
       } catch (Exception ex) { 
        . . . 
       } 
      } 

      if (read.OverviewImages == null) { 
       try { 
        DataInterface.GetOverviewImages(read); 
       } catch (DataAccessException ex) { 
        . . . 
       } catch (Exception ex) { 
        . . . 
       } 
      } 

      if (read.ImageData != null) { 
       string ext = LPRCore.CarSystem.ImageDataAccessor.GetImageFileExtension(read.ImageData); 
       writer.Write("<td width=\"150\"><p align=\"left\" style=\"text-align:left\"><a href=\".\\Images\\{0}.{1}\" target=\"_blank\">BW</a>", read.ID.ToString("N"), ext); 

       string fileName = string.Format(".\\Images\\{0}{1}", read.ID.ToString("N"), ext); 

       if (!zipFile.ContainsEntry(fileName)) { 
        zipFile.AddEntry(fileName, read.ImageData); 
       } 
      } else { 
       writer.Write("No Plate Image"); 
      } 

      if (read.OverviewImages != null && read.OverviewImages.Length > 0) { 
       for (int i = 0; i < read.OverviewImages.Length; i++) { 
        string ext = LPRCore.CarSystem.ImageDataAccessor.GetImageFileExtension(read.OverviewImages[ i ].ImageData); 
        writer.Write(" - <a href=\".\\Images\\{0}_C{1}{2}\" target=\"_blank\">Color {1}</a>", read.ID.ToString("N"), i == 0 ? string.Empty : i.ToString(), ext); 

        string fileName = string.Format(".\\Images\\{0}_c{1}{2}", read.ID.ToString("N"), i == 0 ? string.Empty : i.ToString(), ext); 
        if (!zipFile.ContainsEntry(fileName)) { 
         zipFile.AddEntry(fileName, read.OverviewImages[ i ].ImageData); 
        } 
       } 
      } else { 
       writer.Write("No Overview Images"); 
      } 

      writer.WriteLine("</p></td>"); 
      writer.WriteLine("</tr>"); 
      count++; 
      worker.ReportProgress(count, args); 
     } 
    } 
    writer.WriteLine("</table>"); 
    writer.WriteLine(); 
} 

我在想,也許Zip文件是問題,因爲它沒有被刷新到磁盤或類似的東西上,並且隨着行的處理而變得越來越大。

有沒有一種方法可以將zip文件刷新到磁盤並釋放所有圖像,以便垃圾回收器將它們釋放,或者有另一種方法使用此庫使用較少的內存來構建zip文件?

回答

0

我在這裏的另一位開發人員的幫助下爲此提出了一個修復方案。

DotNetZip庫定義了一個ZipFile.AddEntry方法的重載,該方法將WriteDelegate類型的代理用作參數。委託人通過文件的名稱和文件內容必須寫入的Stream作爲參數。

我定義在我的代碼的方法稱爲GetImageBytes

private void GetImageBytes(string entryName, Stream stream) { 
    Guid imageId; 
    Guid.TryParse(Path.GetFileName(entryName).Substring(0, 32), out imageId); 
    using (BinaryWriter writer = new BinaryWriter(stream)) { 
     try { 
      writer.Write(DataInterface.GetImageData(imageId)); 

     } catch (DataAccessException ex) { 
      DbMonitor.HandleDatabaseError(ex); 

     } catch (Exception ex) { 
      . . . 
     } 
    } 
} 

爲了使這項工作,我不得不把圖像的編碼ID在文件名稱的數據庫。該代碼解析文件名中的ID,然後通過調用服務層中的方法從數據庫中檢索圖像字節。最後,它會創建一個BinaryWriter並將圖像數據發送到Stream

我的程序現在可以輸出超過24,000行的數據,而不會丟出OutOfMemoryException