作为一名C#开发者,你是否经常遇到这样的需求:老板要求将Excel报表数据以图片形式展示在网页上,或者需要将数据表格生成图片用于邮件发送?传统的截图方式不仅效率低下,而且图片质量参差不齐。
这个方案主要还是GDI+其实问题还是不少,没法高保真实现。
在实际开发中,Excel转图片的需求主要来自以下场景:
我们选择EPPlus作为Excel处理库,配合**.NET的GDI+**进行图片渲染,原因如下:
首先安装EPPlus包:
BashInstall-Package EPPlus
C#using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OfficeOpenXml;
namespace AppEPPlus
{
public class ExcelToImageConverter
{
/// <summary>
/// 将Excel工作表的指定区域转换为图片
/// </summary>
/// <param name="excelFilePath">Excel文件路径</param>
/// <param name="outputImagePath">输出图片路径</param>
/// <param name="worksheetIndex">工作表索引(从1开始)</param>
/// <param name="rangeAddress">区域地址,如"A1:C10",为空则转换整个工作表</param>
public static void ConvertExcelToImage(string excelFilePath, string outputImagePath, int worksheetIndex = 1, string rangeAddress = null)
{
// 设置EPPlus许可上下文(非商业用途)
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (var package = new ExcelPackage(new FileInfo(excelFilePath)))
{
// 获取指定工作表
var worksheet = package.Workbook.Worksheets[worksheetIndex - 1]; // 数组索引从0开始
// 确定要转换的区域
ExcelRange targetRange;
if (string.IsNullOrEmpty(rangeAddress))
{
// 如果没有指定区域,使用整个工作表的使用区域
targetRange = worksheet.Cells[worksheet.Dimension.Address];
}
else
{
// 使用指定的区域
targetRange = worksheet.Cells[rangeAddress];
}
// 转换指定区域为图片
SaveRangeAsImage(worksheet, targetRange, outputImagePath);
}
}
/// <summary>
/// 重载方法:使用行列坐标指定区域
/// </summary>
/// <param name="excelFilePath">Excel文件路径</param>
/// <param name="outputImagePath">输出图片路径</param>
/// <param name="worksheetIndex">工作表索引(从1开始)</param>
/// <param name="startRow">起始行(从1开始)</param>
/// <param name="startCol">起始列(从1开始)</param>
/// <param name="endRow">结束行</param>
/// <param name="endCol">结束列</param>
public static void ConvertExcelToImage(string excelFilePath, string outputImagePath, int worksheetIndex,
int startRow, int startCol, int endRow, int endCol)
{
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (var package = new ExcelPackage(new FileInfo(excelFilePath)))
{
var worksheet = package.Workbook.Worksheets[worksheetIndex - 1];
var targetRange = worksheet.Cells[startRow, startCol, endRow, endCol];
SaveRangeAsImage(worksheet, targetRange, outputImagePath);
}
}
/// <summary>
/// 核心方法:将指定区域保存为图片
/// </summary>
private static void SaveRangeAsImage(ExcelWorksheet worksheet, ExcelRange range, string outputPath)
{
if (range == null)
{
Console.WriteLine("指定的区域为空");
return;
}
// 获取区域的行列信息
int startRow = range.Start.Row;
int startCol = range.Start.Column;
int endRow = range.End.Row;
int endCol = range.End.Column;
int rowCount = endRow - startRow + 1;
int colCount = endCol - startCol + 1;
Console.WriteLine($"转换区域:{range.Address} ({rowCount}行 × {colCount}列)");
// 计算图片尺寸
int cellWidth = 100; // 每列像素宽度
int cellHeight = 25; // 每行像素高度
int width = colCount * cellWidth;
int height = rowCount * cellHeight;
// 确保最小尺寸
width = Math.Max(width, 200);
height = Math.Max(height, 100);
using (var bitmap = new Bitmap(width, height))
using (var graphics = Graphics.FromImage(bitmap))
{
// 设置高质量渲染
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
// 填充背景色
graphics.Clear(Color.White);
// 绘制指定区域内容
DrawRangeContent(graphics, worksheet, range, width, height);
// 确保输出目录存在
var directory = Path.GetDirectoryName(outputPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// 保存图片
bitmap.Save(outputPath, ImageFormat.Png);
Console.WriteLine($"区域图片已保存到:{outputPath}");
}
}
/// <summary>
/// 绘制指定区域的内容
/// </summary>
private static void DrawRangeContent(Graphics graphics, ExcelWorksheet worksheet, ExcelRange range, int totalWidth, int totalHeight)
{
int startRow = range.Start.Row;
int startCol = range.Start.Column;
int endRow = range.End.Row;
int endCol = range.End.Column;
int rowCount = endRow - startRow + 1;
int colCount = endCol - startCol + 1;
float cellWidth = (float)totalWidth / colCount;
float cellHeight = (float)totalHeight / rowCount;
// 创建字体和画笔
using (var font = new Font("微软雅黑", 9, FontStyle.Regular))
using (var borderPen = new Pen(Color.Gray, 1))
using (var textBrush = new SolidBrush(Color.Black))
using (var headerBrush = new SolidBrush(Color.LightGray))
{
// 遍历指定区域的所有单元格
for (int row = startRow; row <= endRow; row++)
{
for (int col = startCol; col <= endCol; col++)
{
var cell = worksheet.Cells[row, col];
var cellValue = cell.Text;
// 计算在图片中的位置(相对位置)
float x = (col - startCol) * cellWidth;
float y = (row - startRow) * cellHeight;
// 检查是否有背景色或特殊格式
var fillColor = Color.White;
if (cell.Style.Fill.BackgroundColor.Rgb != null)
{
try
{
string rgbHex = cell.Style.Fill.BackgroundColor.Rgb;
if (rgbHex.Length == 8) // ARGB格式
{
fillColor = Color.FromArgb(
Convert.ToInt32(rgbHex.Substring(0, 2), 16),
Convert.ToInt32(rgbHex.Substring(2, 2), 16),
Convert.ToInt32(rgbHex.Substring(4, 2), 16),
Convert.ToInt32(rgbHex.Substring(6, 2), 16));
}
}
catch
{
fillColor = Color.White;
}
}
// 填充单元格背景
if (fillColor != Color.White)
{
using (var backgroundBrush = new SolidBrush(fillColor))
{
graphics.FillRectangle(backgroundBrush, x, y, cellWidth, cellHeight);
}
}
// 绘制单元格边框
graphics.DrawRectangle(borderPen, x, y, cellWidth, cellHeight);
// 绘制单元格内容
if (!string.IsNullOrEmpty(cellValue))
{
var rect = new RectangleF(x + 3, y + 3, cellWidth - 6, cellHeight - 6);
var stringFormat = new StringFormat
{
Alignment = StringAlignment.Near,
LineAlignment = StringAlignment.Center,
FormatFlags = StringFormatFlags.NoWrap,
Trimming = StringTrimming.EllipsisCharacter
};
// 根据单元格样式设置文本颜色
var textColor = Color.Black;
if (cell.Style.Font.Color.Rgb != null)
{
try
{
string rgbHex = cell.Style.Font.Color.Rgb;
if (rgbHex.Length == 8)
{
textColor = Color.FromArgb(
Convert.ToInt32(rgbHex.Substring(2, 2), 16),
Convert.ToInt32(rgbHex.Substring(4, 2), 16),
Convert.ToInt32(rgbHex.Substring(6, 2), 16));
}
}
catch
{
textColor = Color.Black;
}
}
using (var currentTextBrush = new SolidBrush(textColor))
{
graphics.DrawString(cellValue, font, currentTextBrush, rect, stringFormat);
}
}
}
}
}
}
}
}
C#namespace AppEPPlus
{
internal class Program
{
static void Main(string[] args)
{
// 调用方法
string excelFile = @"./report.xlsx";
string outputImage = @"./report.png";
ExcelToImageConverter.ConvertExcelToImage(excelFile, outputImage, 1, "A1:C12");
Console.WriteLine("Excel转换完成!");
}
}
}

using语句确保图形资源正确释放C#// ✅ 推荐:处理大文件时的内存优化
public static void ConvertLargeExcelToImage(string excelPath, string outputPath, int maxRows = 1000)
{
// 分批处理,避免内存溢出
// 具体实现可根据实际需求调整
}
问题:EPPlus 5.0+版本需要设置许可证上下文
解决:添加ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
问题:生成的图片中文显示异常
解决:明确指定中文字体如"微软雅黑"
问题:输出图片模糊或锯齿严重
解决:设置高质量渲染参数(代码中已包含)
你在项目中是如何处理Excel数据展示需求的?遇到过哪些技术难点?欢迎在评论区分享你的经验!
觉得有用请转发给更多同行,让更多C#开发者受益!
关注我,获取更多C#开发实战技巧和最佳实践!
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!