C#导入EXCEL时如何读取单元格中的图片(WPS)
作者:@梦想家@
C#导入EXCEL时读取单元格中的图片(WPS)
起因
最新要做一个excel批量导入商品的功能,但是文本内容好读取,但是图片文件在网上没有找到比较好的解决办法
解决方案1
(不推荐)先导入excel数据,再通过文件夹/压缩包的方式导入图片,但是这样比较麻烦,如果说excel表格后续还需要维护的话,对于做excel表格的同学来说不太友好
解决方案2
解压缩excel文件,可以得到对应的文件夹,如下图所示的内容,其中xl文件夹下存在几个数据文件,可以手动拼接起来,下面着重说一下该如何一步步实现指定单元格的文件读取
1)上传excel文件
定义好接受类,我的类是这样,可以根据自己需要进行修改
/// <summary> /// 导入产品dto /// </summary> public class ImportSoftProductDto:PrimaryKey { /// <summary> /// 供应商编号 /// </summary> [ExcelColumnIndex("A")] public string SupplierNo { get; set; } /// <summary> /// 产品编号 /// </summary> [ExcelColumnIndex("B")] public string ProductNo { get; set; } /// <summary> /// 产品名称 /// </summary> [ExcelColumnIndex("C")] public string ProductName { get; set; } /// <summary> /// 主分类 /// </summary> [ExcelColumnIndex("D")] public string MainCategory { get; set; } /// <summary> /// 次分类 /// </summary> [ExcelColumnIndex("E")] public string SubCategory { get; set; } /// <summary> /// 采购单价 /// </summary> [ExcelColumnIndex("F")] public decimal PurchasePrice { get; set; } /// <summary> /// 销售单价 /// </summary> [ExcelColumnIndex("G")] public decimal SalesPrice { get; set; } /// <summary> /// 单位 /// </summary> [ExcelColumnIndex("H")] public string Unit { get; set; } /// <summary> /// 尺寸 /// </summary> [ExcelColumnIndex("I")] public string Size { get; set; } /// <summary> /// 材质报价 /// </summary> [ExcelColumnIndex("J")] public string Texture { get; set; } /// <summary> /// 备注说明 /// </summary> [ExcelColumnIndex("K")] public string Explain { get; set; } /// <summary> /// 图片_DISPIMG函数字符串 /// </summary> [ExcelColumnIndex("L")] public string IMG_DISPIMG { get; set; } //= DISPIMG("ID_498B62A4F63D447D9F9E1640DAF57A45", 1) public string DISPIMG_Id { get { if (!string.IsNullOrWhiteSpace(IMG_DISPIMG)) { var split = IMG_DISPIMG.Split('"'); return split[1]; } else { return ""; } } } /// <summary> /// 图片原始路径 /// </summary> public string ImgOriginalUrl { get; set; } /// <summary> /// 图片最终路径 /// </summary> public string ImgFinalUrl { get; set; } public string ImgSuffix { get { if (!string.IsNullOrWhiteSpace(ImgOriginalUrl)) { return Path.GetExtension(ImgOriginalUrl).ToLower().Replace(".", ""); } else { return string.Empty; } } } }
2)读取excel文件内容,这里使用的是MiniExcel,可以在nuget中安装
注意:wps嵌入的图片可以通过string得到如下的字符串=DISPIMG("ID_8C72DF5CE3FA413298278785876E9D65",1),其中,ID_8C72DF5CE3FA413298278785876E9D65是我们需要的东东
var rows = MiniExcel.Query<ImportSoftProductDto>(absoluteUrl, startCell: "A3").ToList();
3)解压缩excel
需要用到命名空间:System.IO.Compression,这里,我是直接以该excel的文件名作为解压文件夹的
var zipFileDirectory = absoluteUrl.Substring(0, absoluteUrl.LastIndexOf(".")); ZipFile.ExtractToDirectory(absoluteUrl, zipFileDirectory);
4)读取cellimages.xml内容,该文件位于xl文件夹下
var cellimagesXML = File.ReadAllText(Path.Combine(zipFileDirectory, "xl/cellimages.xml"));
5)读取cellimages.xml.rels内容
var cellimagesXMLRels = File.ReadAllText(Path.Combine(zipFileDirectory, "xl/_rels/cellimages.xml.rels"));
6)结合2个文件内容和图片资源名称
计算出第二步中ID_8C72DF5CE3FA413298278785876E9D65对应的图片,这里也可以自己解析xml内容,主要是为了获得他们直接的对应关系
var imgList = new List<ImportSoftProductImgDto>(); var nodes_XML = XElement.Parse(cellimagesXML); foreach (var xNode in nodes_XML.DescendantNodes().OfType<XCData>().ToList()) { xNode.Parent.Add(xNode.Value); xNode.Remove(); } var json_XML = JObject.Parse(JsonConvert.SerializeXNode(nodes_XML, Formatting.Indented)); foreach (var item in json_XML["etc:cellImages"]["etc:cellImage"]) { var DISPIMG_Id = item["xdr:pic"]["xdr:nvPicPr"]["xdr:cNvPr"]["@name"].ToString(); var id = item["xdr:pic"]["xdr:blipFill"]["a:blip"]["@r:embed"].ToString(); imgList.Add(new ImportSoftProductImgDto { Id = id, Target = string.Empty, DISPIMG_Id = DISPIMG_Id }); } var nodes_XMLRels = XElement.Parse(cellimagesXMLRels); foreach (var xNode in nodes_XMLRels.DescendantNodes().OfType<XCData>().ToList()) { xNode.Parent.Add(xNode.Value); xNode.Remove(); } var json_XMLRels = JObject.Parse(JsonConvert.SerializeXNode(nodes_XMLRels, Formatting.Indented)); foreach (var item in json_XMLRels["Relationships"]["Relationship"]) { var id = item["@Id"].ToString(); var target = item["@Target"].ToString(); //可能公用一张图片 foreach (var imgItem in imgList.Where(o => o.Id == id)) { imgItem.Target = target; } }
7)遍历赋值图片路径
foreach (var item in rows) { item.ImgOriginalUrl = imgList.FirstOrDefault(o => o.DISPIMG_Id == item.DISPIMG_Id)?.Target; }
8)接下来就是图片上传到新地址的方式了
这里是我的上传实现
foreach (var item in rows) { if (!string.IsNullOrWhiteSpace(item.ImgOriginalUrl)) { var suffix = Path.GetExtension(item.ImgOriginalUrl).ToLower().Replace(".", "");//文件后缀 var relativePath = Path.Combine("uploads", suffix, DateTime.Now.ToString("yyyyMM"));//相对路径 var fileName = Guid.NewGuid().ToString("N") + "." + suffix;//图片重命名 var absolutePath = Path.Combine(App.HostEnvironment.ContentRootPath, relativePath);//绝对路径 if (!Directory.Exists(absolutePath)) { Directory.CreateDirectory(absolutePath); } FileInfo file = new FileInfo(Path.Combine(zipFileDirectory, "xl", item.ImgOriginalUrl)); if (file.Exists) //可以判断源文件是否存在 { // 这里是true的话覆盖 file.CopyTo(Path.Combine(absolutePath, fileName), true); item.ImgFinalUrl = Path.Combine(_appInfo.Host, relativePath, fileName).Replace("\\", "/"); } } }
9)至此,就可以往数据库里插入数据了
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。