欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C# Npoi如何讀取單元格圖片并獲取所在單元格位置

 更新時(shí)間:2025年04月29日 09:43:25   作者:umarururururu  
這篇文章主要介紹了C# Npoi如何讀取單元格圖片并獲取所在單元格位置,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

C# Npoi讀取單元格圖片并獲取所在單元格位置

C#在excel中讀取圖片 獲取圖片的單元格位置信息(僅限于xlsx)

主要的代碼邏輯

  1. 將excel使用zip直接打開
  2. 壓縮包內(nèi)圖片信息主要存放在兩個(gè)文件夾中(xl/media,xl/drawings)前者是圖片本身,后者是圖片的主要信息
  3. 在drawings文件夾下會(huì)存在若干以drawing開頭的.xml文件,一個(gè)sheet頁(yè)對(duì)應(yīng)一個(gè)drawing.xml后綴按照數(shù)字序號(hào)遞增(例如:第一頁(yè)為drawing1.xml,第二頁(yè)為drawing2.xml以此類推)
  4. 打開drawing.xml后,讀取文檔節(jié)點(diǎn),節(jié)點(diǎn)含義對(duì)照表在末尾
  5. 在pic標(biāo)簽中獲取到圖片的rid后通過(guò)該id在同級(jí)目錄下的_rels文件夾下找到對(duì)應(yīng)的drawing.xml.rels中讀取圖片的存放地址(例如drawing1.xml對(duì)應(yīng)drawing1.xml.rels)
twoCellAnchor圖片信息的節(jié)點(diǎn) 節(jié)點(diǎn)下包含了圖片的起始單元格位置 圖片的id即[r:embed]的內(nèi)容 存在多少個(gè)[twoCellAnchor]即存在多少個(gè)圖片
twoCellAnchor/from圖片左上角開始位置單元格信息
twoCellAnchor/to圖片右下角結(jié)束位置單元格信息 (當(dāng)前需求需要盡量保證一張圖片存在一個(gè)單元格中,不允許跨單元格 因?yàn)椴粫?huì)對(duì)其做其他的處理)
twoCellAnchor/pic圖片的文件信息 主要讀取 [blipFill->blip->r:embed]此信息為圖片的id信息可以讀取到文件在media文件夾中存放的位置
twoCellAnchor/xfrm待確定該信息,主要猜想為圖片的大小?
twoCellAnchor/prstGeom圖片的插入方式

以下是封裝完成的代碼

 public class ExcelImgHelper
 {
     #region 常量

     /// <summary>
     /// 文件id與文件路徑文件夾路徑
     /// </summary>
     private const string DrawingRels = "xl/drawings/_rels/drawing_id_.xml.rels";

     /// <summary>
     /// 圖片信息文件
     /// </summary>
     private const string Drawing = "xl/drawings/drawing_id_.xml";

     #region 圖片信息主要標(biāo)簽

     private const string twoCellAnchor = "twoCellAnchor";
     private const string embed = "embed";
     private const string link = "link";
     private const string prst = "prst";

     #endregion 圖片信息主要標(biāo)簽

     #endregion 常量

     #region 路徑信息

     /// <summary>
     /// excel文件地址
     /// </summary>
     private string ExcelPath { get; }

     /// <summary>
     /// 解壓的文件夾
     /// </summary>
     private string ExcelZipPath { get; }

     /// <summary>
     /// 壓縮包 *注意 與上方需要區(qū)分開當(dāng)前路勁是壓縮文件包.zip
     /// </summary>
     private string ExcelZipFilePath { get; }

     #endregion 路徑信息

     private List<ExcelImgInfo> ExcelImgInfos = new List<ExcelImgInfo>();

     public ExcelImgHelper(string filePath)
     {
         if (string.IsNullOrEmpty(filePath))
         {
             throw new ArgumentNullException(nameof(filePath));
         }

         //解壓后文件夾存放的位置 與源文件同目錄
         var dir = Path.GetDirectoryName(filePath);
         //獲取文件名
         var fileName = Path.GetFileNameWithoutExtension(filePath);
         //壓縮包路徑
         var zipFilePath = dir + "\\" + fileName + ".zip";

         //復(fù)制為壓縮包
         File.Copy(filePath, zipFilePath);

         //解壓文件
         if (UnZipFile(filePath, out string UnZipFilePath))
         {
             ExcelPath = filePath;
             ExcelZipPath = UnZipFilePath;
             ExcelZipFilePath = zipFilePath;

             ExcelImgInfos = Analysis();
         }
         else
         {
             throw new Exception("解壓失敗");
         }
     }

     /// <summary>
     /// 解析excel中的圖片
     /// </summary>
     /// <returns></returns>
     private List<ExcelImgInfo> Analysis()
     {
         List<ExcelImgPathAndId> imgs = new List<ExcelImgPathAndId>();
         List<ExcelImgInfo> excelImgInfos = new List<ExcelImgInfo>();

         //讀取所有圖片以及位置信息
         FindPicPathByID(ref imgs);

         //默認(rèn)命名空間
         XNamespace xdr_namespace = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing";
         XNamespace a_namespace = "http://schemas.openxmlformats.org/drawingml/2006/main";
         XNamespace r_namespace = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";

         //加載圖片信息文檔xml(替換的文件名與頁(yè)數(shù)對(duì)應(yīng) 此處可以優(yōu)化為傳入指定id)
         XDocument xdoc = XDocument.Load(ExcelZipPath + Drawing.Replace("_id_", "1"));

         //加載文檔中的默認(rèn)命名空間
         var root = xdoc.Root;
         foreach (var item in root.Attributes())
         {
             if (item.Name.LocalName == "xdr")
             {
                 xdr_namespace = item.Value;
             }
             else if (item.Name.LocalName == "a")
             {
                 a_namespace = item.Value;
             }
         }

         //讀取twoCellAnchor標(biāo)簽中的內(nèi)容 **核心部分**
         foreach (var node in xdoc.Descendants(xdr_namespace + twoCellAnchor))
         {
             //twoCellAnchor標(biāo)簽中子標(biāo)簽內(nèi)容順序永遠(yuǎn)為:from->to->pic
             //所以此處順序讀取即可
             var NodeFrom = (XElement)node.FirstNode;
             var NodeTo = (XElement)NodeFrom.NextNode;
             var NodePic = (XElement)NodeTo.NextNode;

             //找到blipFill節(jié)點(diǎn),并找到r節(jié)點(diǎn)的命名空間
             var blipFill = (XElement)(((XElement)NodePic.FirstNode.NextNode).FirstNode);
             r_namespace = blipFill.FirstAttribute.IsNamespaceDeclaration ? blipFill.FirstAttribute.Value : r_namespace;

             //找到spPr節(jié)點(diǎn)
             var spPr = (XElement)NodePic.FirstNode.NextNode.NextNode;

             //獲取圖片Id
             var ImgId = (blipFill.Attribute(r_namespace + embed) != null ? blipFill.Attribute(r_namespace + embed) : blipFill.Attribute(r_namespace + link)).Value.ToString();

             //獲取from
             var From = new Position()
             {
                 Col = int.Parse(((XElement)NodeFrom.FirstNode).Value),
                 ColOff = int.Parse(((XElement)NodeFrom.FirstNode.NextNode).Value),
                 Row = int.Parse(((XElement)NodeFrom.FirstNode.NextNode.NextNode).Value),
                 RowOff = int.Parse(((XElement)NodeFrom.FirstNode.NextNode.NextNode.NextNode).Value)
             };
             //獲取to
             var To = new Position()
             {
                 Col = int.Parse(((XElement)NodeTo.FirstNode).Value),
                 ColOff = int.Parse(((XElement)NodeTo.FirstNode.NextNode).Value),
                 Row = int.Parse(((XElement)NodeTo.FirstNode.NextNode.NextNode).Value),
                 RowOff = int.Parse(((XElement)NodeTo.FirstNode.NextNode.NextNode.NextNode).Value)
             };

             //獲取圖片插入方式
             var PrstGeom = ((XElement)spPr.FirstNode.NextNode).Attribute(prst).Value.ToString();

             //
             var xfrm = ((XElement)spPr.FirstNode);
             var xfrm_off = ((XElement)xfrm.FirstNode);
             var xfrm_ext = ((XElement)xfrm.FirstNode.NextNode);
             List<int> xfrm_offData = new List<int>
                 {
                     int.Parse(xfrm_off.Attribute("x").Value.ToString()),
                     int.Parse(xfrm_off.Attribute("y").Value.ToString())
                 };
             List<int> xfrm_extData = new List<int>
                 {
                     int.Parse(xfrm_ext.Attribute("cx").Value.ToString()),
                     int.Parse(xfrm_ext.Attribute("cy").Value.ToString())
                 };

             //獲取圖片實(shí)際位置
             var PathOfPicture = imgs.FirstOrDefault(e => e.Id == ImgId)?.Path;
             //此處圖片為相對(duì)位置需要處理成為絕對(duì)路徑
             PathOfPicture = PathOfPicture.Replace("../", ExcelZipPath + "xl\\").Replace("/", "\\");

             //至此 所有需要使用的節(jié)點(diǎn)全部取出 開始組裝數(shù)據(jù)
             ExcelImgInfo excelImgInfo = new ExcelImgInfo(
                 imgId: ImgId,
                 from: From,
                 to: To,
                 prstGeom: PrstGeom,
                 xfrm_off: xfrm_offData,
                 xfrm_ext: xfrm_extData,
                 pathOfPicture: PathOfPicture);

             excelImgInfos.Add(excelImgInfo);
         }
         //Dispose();
         return excelImgInfos;
     }

     /// <summary>
     /// 解壓文件
     /// </summary>
     /// <param name="zipFilePath">壓縮文件路徑</param>
     /// <param name="path">返回壓縮文件夾路徑</param>
     /// <param name="unZipDir">解壓文件存放路徑,為空時(shí)默認(rèn)與壓縮文件同一級(jí)目錄下,跟壓縮文件同名的文件夾</param>
     /// <returns></returns>
     private bool UnZipFile(string zipFilePath, out string path, string unZipDir = null)
     {
         if (zipFilePath == string.Empty)
         {
             path = null;
             return false;
         }

         if (!System.IO.File.Exists(zipFilePath))
         {
             path = null;
             return false;
         }
         //解壓文件夾為空時(shí)默認(rèn)與壓縮文件同一級(jí)目錄下,跟壓縮文件同名的文件夾
         if (string.IsNullOrWhiteSpace(unZipDir))
             unZipDir = zipFilePath.Replace(Path.GetFileName(zipFilePath), Path.GetFileNameWithoutExtension(zipFilePath));

         if (!unZipDir.EndsWith("\\"))
             unZipDir += "\\";

         if (!Directory.Exists(unZipDir))
             Directory.CreateDirectory(unZipDir);
         try
         {
             using (ZipInputStream s = new ZipInputStream(System.IO.File.OpenRead(zipFilePath)))
             {
                 ZipEntry theEntry;
                 while ((theEntry = s.GetNextEntry()) != null)
                 {
                     string directoryName = Path.GetDirectoryName(theEntry.Name);
                     string fileName = Path.GetFileName(theEntry.Name);
                     if (directoryName.Length > 0)
                     {
                         Directory.CreateDirectory(unZipDir + directoryName);
                     }
                     if (!directoryName.EndsWith("\\"))
                         directoryName += "\\";
                     if (fileName != String.Empty)
                     {
                         using (FileStream streamWriter = System.IO.File.Create(unZipDir + theEntry.Name))
                         {
                             int size = 2048;
                             byte[] data = new byte[2048];
                             while (true)
                             {
                                 size = s.Read(data, 0, data.Length);
                                 if (size > 0)
                                 {
                                     streamWriter.Write(data, 0, size);
                                 }
                                 else
                                 {
                                     break;
                                 }
                             }
                         }
                     }
                 }
             }
         }
         catch
         {
             path = null;
             return false;
         }
         path = unZipDir;

         return true;
     }

     /// <summary>
     /// 獲取全部文件id與路徑
     /// </summary>
     /// <param name="imgs"></param>
     /// <param name="_id"></param>
     private void FindPicPathByID(ref List<ExcelImgPathAndId> imgs, int _id = 1)
     {
         string _file = Path.Combine(ExcelZipPath + DrawingRels.Replace("_id_", _id.ToString()));

         if (!File.Exists(_file))
         {
             throw new DirectoryNotFoundException(_file);
         }

         XDocument xDoc = XDocument.Load(_file);
         var root = xDoc.Root;
         foreach (XElement node in root.Nodes())
         {
             var attrs = node.Attributes();
             string Id = "";
             string Target = "";
             foreach (var attr in attrs)
             {
                 if (attr.Name == "Id")
                     Id = attr.Value.ToString();
                 else if (attr.Name == "Target")
                     Target = attr.Value.ToString();
             }
             imgs.Add(new ExcelImgPathAndId()
             {
                 Id = Id,
                 Path = Target
             });
         }
     }

     /// <summary>
     /// 獲取excel圖片以及位置信息
     /// </summary>
     /// <returns></returns>
     public List<ExcelImgInfo> GetAllImgs()
     {
         return ExcelImgInfos;
     }

     /// <summary>
     /// 刪除解壓的文件
     /// </summary>
     public void Dispose()
     {
         File.Delete(ExcelZipFilePath);
         DirectoryInfo di = new DirectoryInfo(ExcelZipPath);
         di.Delete(true);
     }
 }

需要用到的輔助類

 /// <summary>
 /// 提取出來(lái)的圖片信息類
 /// </summary>
 public class ExcelImgInfo
 {
     public ExcelImgInfo()
     {
     }

     public ExcelImgInfo(string imgId, Position from, Position to, string prstGeom, List<int> xfrm_off, List<int> xfrm_ext, string pathOfPicture)
     {
         try
         {
             ImgId = imgId;
             From = from;
             To = to;
             PrstGeom = prstGeom;
             this.xfrm_off = xfrm_off;
             this.xfrm_ext = xfrm_ext;
             PathOfPicture = pathOfPicture;

             if (File.Exists(PathOfPicture))
             {
                 //將圖片讀取到內(nèi)存中并且不鎖定文件
                 FileStream fileStream = new FileStream(PathOfPicture, FileMode.Open, FileAccess.Read);

                 int byteLength = (int)fileStream.Length;
                 byte[] fileBytes = new byte[byteLength];
                 fileStream.Read(fileBytes, 0, byteLength);
                 fileStream.Close();

                 using (MemoryStream ms = new MemoryStream(fileBytes))
                 {
                     ms.Write(fileBytes, 0, fileBytes.Length);
                     ExcelImage = Image.FromStream(ms, true);
                 }
                 imgByteArray = fileBytes;
             }
             else
             {
                 throw new FileNotFoundException("圖片位置錯(cuò)誤");
             }
         }
         catch (Exception e)
         {
             throw new Exception($" 圖片對(duì)象初始化時(shí)錯(cuò)誤:[{imgId}]\n{e.Message}\n{e.StackTrace}");
         }
     }

     /// <summary>
     /// 圖片Id
     /// </summary>
     public string ImgId { get; protected set; }

     /// <summary>
     /// 開始的單元格
     /// </summary>
     public Position From { get; protected set; }

     /// <summary>
     /// 結(jié)束的單元格
     /// </summary>
     public Position To { get; protected set; }

     /// <summary>
     /// 圖片插入方式
     /// </summary>
     public string PrstGeom { get; protected set; }

     /// <summary>
     /// [0]:x
     /// [1]:y
     /// </summary>
     public List<int> xfrm_off { get; protected set; } = new List<int>();

     /// <summary>
     /// [0]:cx
     /// [1]:cy
     /// </summary>
     public List<int> xfrm_ext { get; protected set; } = new List<int>();

     /// <summary>
     /// 圖片地址
     /// </summary>
     public string PathOfPicture { get; protected set; }

     /// <summary>
     /// 圖片數(shù)據(jù)
     /// </summary>
     public Image ExcelImage { get; protected set; }

     /// <summary>
     /// 圖片數(shù)組
     /// </summary>
     public byte[] imgByteArray { get; protected set; }

     public void Dispose()
     {
         //ExcelImage.Dispose();
     }
 }
 /// <summary>
 /// 文件Id與路徑
 /// </summary>
 public class ExcelImgPathAndId
 {
     public string Id { get; set; }

     public string Path { get; set; }
 }
 /// <summary>
 /// 位置信息
 /// </summary>
 public class Position
 {
     public int Col { get; set; }
     public int ColOff { get; set; }
     public int Row { get; set; }
     public int RowOff { get; set; }
 }

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • C#調(diào)用Oracle存儲(chǔ)過(guò)程方法介紹(附源碼)

    C#調(diào)用Oracle存儲(chǔ)過(guò)程方法介紹(附源碼)

    這篇文章介紹了C#調(diào)用Oracle存儲(chǔ)過(guò)程的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03
  • C#對(duì)文件進(jìn)行批量重命名或者對(duì)某單個(gè)文件進(jìn)行改名的示例代碼

    C#對(duì)文件進(jìn)行批量重命名或者對(duì)某單個(gè)文件進(jìn)行改名的示例代碼

    這篇文章主要介紹了C#對(duì)文件進(jìn)行批量重命名或者對(duì)某個(gè)單獨(dú)的文件進(jìn)行改名的實(shí)現(xiàn)方法,文中有相關(guān)的代碼示例供大家參考,具有一定的參考價(jià)值,需要的朋友可以參考下
    2024-05-05
  • 一文詳解Unity3D?AudioSource組件使用示例

    一文詳解Unity3D?AudioSource組件使用示例

    這篇文章主要為大家介紹了一文詳解Unity3D?AudioSource組件使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • Unity3D實(shí)戰(zhàn)之答題系統(tǒng)的實(shí)現(xiàn)

    Unity3D實(shí)戰(zhàn)之答題系統(tǒng)的實(shí)現(xiàn)

    本文將用Unity3D制作一個(gè)答題系統(tǒng),可以從文本文檔中提取題目和分?jǐn)?shù),然后綁定到UI上,在答題的過(guò)程中,自動(dòng)判斷分?jǐn)?shù),自動(dòng)判斷正確率。感興趣的可以學(xué)習(xí)一下
    2022-03-03
  • C#操作配置文件app.config、web.config增刪改

    C#操作配置文件app.config、web.config增刪改

    這篇文章介紹了C#操作配置文件app.config、web.config增刪改的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • WinForm DataGridView控件隔行變色的小例子

    WinForm DataGridView控件隔行變色的小例子

    WinForm的DataGridView控件設(shè)置行的顏色
    2013-03-03
  • 實(shí)例詳解C#正則表達(dá)式

    實(shí)例詳解C#正則表達(dá)式

    這篇文章主要通過(guò)實(shí)例詳解C#正則表達(dá)式的相關(guān)資料,需要的朋友可以參考下
    2016-01-01
  • C#多線程系列之線程通知

    C#多線程系列之線程通知

    本文詳細(xì)講解了C#多線程中的線程通知,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • 使用C#實(shí)現(xiàn)解析Excel公式

    使用C#實(shí)現(xiàn)解析Excel公式

    在日常工作中,我們經(jīng)常需要在Excel中使用公式對(duì)表中數(shù)據(jù)進(jìn)行計(jì)算和分析,所以本文小編主要來(lái)和大家介紹一下如何在C#中實(shí)現(xiàn)解析Excel公式,感興趣的可以了解下
    2024-02-02
  • C#的泛型方法解析

    C#的泛型方法解析

    本文講解了C#2.0引入的泛型知識(shí),主要包含泛型類、泛型接口、泛型委托,并且重點(diǎn)講解了泛型方法,已經(jīng)泛型的約束分類。最后給了一些利用泛型方法操作xml的方法。希望對(duì)大家有所幫助
    2016-12-12

最新評(píng)論