最近客户突然让整理所有施工站点的完工照片,他们需要留档,所以不想通过平台直接查看,想把所有完工照片,按照操作步骤把图片放在word文档中。一个市一千多个站点要用人工去做,那一个人也得一个月。所以就写了一个简单的程序,动态生成word的方式。信息化统计肯定比人效率高。
我选择了NPOI插件做的,因为也就是一次性,我就直接用控制台简单的实现了一下。不多说直接上代码:
这次的逻辑是记录郑州市各个区域的站点。所以要分区域见文件夹,各个站点的word文档就放在各个区域的文件夹下。
using NPOI.OpenXmlFormats.Wordprocessing; using NPOI.Util; using NPOI.XWPF.UserModel; using System; using System.Data; using System.Drawing; using System.IO; using System.Net; using System.Threading.Tasks; namespace WordExportDemo { internal class Program { private static void Main(string[] args) { DataTable dtlist = SqlHelper.Query(" SELECT PTId,PId,TaskName,TaskLable,TaskTemplateId ,JZ_Id ,JZ_Type,(select AreaName from Sys_Area where AreaCode=JZ_AREAID) AreaName FROM Project_Task where TaskType=2 and PId=8 and JZ_Type!=3 and State>0 and JZ_CityID=410100"); foreach (DataRow item in dtlist.Rows) { string areaname = item["AreaName"].ToString().Trim();//城市名称 string taskName = item["TaskName"].ToString().Trim();//任务名称,或者城市名称 int Temple = int.Parse(item["PTId"].ToString());//任务模板id string path = System.AppDomain.CurrentDomain.BaseDirectory + "郑州市\" + areaname;//拼接路径 CreatePath(path);//判断路径是否存在,不存在先建路径 path = path + "\" + taskName + ".docx";//给word文档命名 XWPFDocument doc = new XWPFDocument();//创建一个word文档 // 添加段落 XWPFParagraph gp = doc.CreateParagraph(); gp.Alignment = ParagraphAlignment.CENTER;//水平居中 XWPFRun gr = gp.CreateRun(); gp = doc.CreateParagraph(); gr = gp.CreateRun(); gr.GetCTR().AddNewRPr().AddNewRFonts().ascii = "黑体"; gr.GetCTR().AddNewRPr().AddNewRFonts().eastAsia = "黑体"; gr.GetCTR().AddNewRPr().AddNewRFonts().hint = ST_Hint.eastAsia; gr.GetCTR().AddNewRPr().AddNewSz().val = (ulong)42;//2号字体 gr.GetCTR().AddNewRPr().AddNewSzCs().val = (ulong)42; gr.GetCTR().AddNewRPr().AddNewB().val = true; //加粗 gr.GetCTR().AddNewRPr().AddNewColor().val = "red";//字体颜色 gr.SetText("站点名称:" + taskName); DataTable dttemple = SqlHelper.Query("SELECT Id,Description ,AddTime ,TempStepId,(select Description from Project_TemplateStep where Id=TempStepId) StepDescription ,OrderNum FROM Project_TaskStep where PTId=" + Temple + " order by OrderNum asc"); foreach (DataRow item1 in dttemple.Rows) { int tempid = int.Parse(item1["Id"].ToString());//步骤Id string TempName = item1["StepDescription"].ToString();//步骤名称 int step = int.Parse(item1["OrderNum"].ToString());//步骤数 string stepDescrip = item1["Description"].ToString();//步骤描述 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //添加任务步骤描述 gp = doc.CreateParagraph(); gr = gp.CreateRun(); CT_RPr rpr = gr.GetCTR().AddNewRPr(); CT_Fonts rfonts = rpr.AddNewRFonts(); rfonts.ascii = "宋体"; rfonts.eastAsia = "宋体"; rpr.AddNewSz().val = (ulong)21;//5号字体 rpr.AddNewSzCs().val = (ulong)21; gr.SetText("步骤" + step + ":" + TempName + " 描述:" + stepDescrip); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// DataTable dtimg = SqlHelper.Query("SELECT TableName,Path,AddTime FROM Project_TaskFile where TableName='Project_TaskStep' and TableKey=" + tempid + " order by AddTime asc"); gp = doc.CreateParagraph(); gr = gp.CreateRun(); foreach (DataRow item3 in dtimg.Rows) { string s = "ip" + item3["Path"].ToString(); try { HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://ip" + item3["Path"].ToString()); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream imgstream = response.GetResponseStream();//获取图片的流 Image image = Image.FromStream(imgstream);//将流转化成Image,这样做是为了得到原图的宽(image.Width)和高(image.Height),放在word中可以通过宽和高等比缩放,这样就可以保证图片不变形。 gr.AddPicture(ImgToStream(image), (int)PictureType.PNG, "1.png", image.Width * 800, image.Height * 800);//ImgToTream(image) } catch (Exception e) { Console.WriteLine("---------------------------------------------------------------------------------------------------"); Console.WriteLine("区域:" + areaname + "--任务名称:" + taskName + "--" + "步骤" + tempid + "--这个图片路径有异常:URL=" + s); Console.WriteLine("---------------------------------------------------------------------------------------------------"); } } } using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write)) { doc.Write(fs); Console.WriteLine("生成word成功"); } } Console.ReadKey(); } } }
下面是远程下载图片并且将图片插入word中,
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://ip" + item3["Path"].ToString()); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream imgstream = response.GetResponseStream();//获取图片的流 Image image = Image.FromStream(imgstream); gr.AddPicture(ImgToStream(image), (int)PictureType.PNG, "1.png", image.Width * 800, image.Height * 800);
AddPicture这个是NPOI里面的程序集,他提供了传入图片的方式是一个Stream流: pictureData:表示传入一个图片的流。 pictureType:表示图片的类型:png,jpeg..... filename:图片的名字.图片的宽。height:图片的高。
或许有人会问,既然传入的是一个流,为什么还有用Imag转化成流在传入,远程下载的时候就是一个流,直接传入不就行了。我一开始也是这样做的。但是我必须要先转换成Image,不然我无法得到原图的宽和高。有人或许还会问,那转成Image也不影响把流传入进去。我试过了,这个流一旦转换成Image之后,这个流的长度就是0,如果作为参数传入AddPicture方法会报异常。我也试了先把流转换成字节存起来,然后利用字节转换成Image和Stream流,但是也失败了,所有我最后选择先用Stream流转换成Image得到宽和高后再把Image转换为字节,通过字节在转换成流,结果成功了。
判断路径是否存在的方法
/// <summary> /// 创建目录,如果目录不存在就创建,存在则直接返回。 /// </summary> /// <param name="path"></param> public static string CreatePath(string strPath) { if (!System.IO.Directory.Exists(strPath)) System.IO.Directory.CreateDirectory(strPath); return strPath; }
图片转换成字节流
/// <summary> /// 图片转换成字节流 /// </summary> /// <param name="img">要转换的Image对象</param> /// <returns>转换后返回的字节流</returns> public static Stream ImgToStream(Image img) { byte[] imgByte = ImgToByt(img); //img.Save(ms, img.RawFormat);//System.Drawing.Imaging.ImageFormat.Jpeg Stream stream = new MemoryStream(imgByte); return stream; } /// <summary> /// 图片转换成字节流 /// </summary> /// <param name="img">要转换的Image对象</param> /// <returns>转换后返回的字节流</returns> public static byte[] ImgToByt(Image img) { MemoryStream ms = new MemoryStream(); byte[] imagedata = null; img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); imagedata = ms.GetBuffer(); return imagedata; }
运行后结果: