需求
曾经的一个项目,要求实现制作电子期刊定期发送给企业进行阅读,基本的需求如下:
1、由编辑人员使用 Microsoft Word 编辑期刊内容,上传到系统,生成PDF文件。
2、将生成的PDF文件转化为JPEG文件。
3、将JPEG文件制作目录结构,并生成电子书模式。
方案分析
分析了一下需求,制作了初步的实现方案,主要有以下几点分析:
1、Microsoft Word 仍然是目前比较常用和广泛使用的应用程序,适用于各类人群,他们只需要编写好自己的文稿即可(包括文字、图片、排版),所以可以作为实现电子书的基础。
2、较高版本的 Word 如2016、2019及以上,可以提供另存为PDF的能力,利用API可以将DOCX另存为PDF文件,为进一步生成JPEG图片提供基础。
3、利用改造过的 turn.js 实现电子书及翻页效果。
相关库引入
实现功能要引入相关库,包括 PdfiumViewer.dll 和 turn.js 相关包,完整下载链接请访问:
https://download.csdn.net/download/michaelline/88647689
另外,在服务器端您需要安装 Microsoft Word 2016 或以上版本。
关键代码
Word 转 Pdf
在操作界面,上传WORD文件,通过API将其另存为PDF文件。
示例代码如下:
public string WordToPdf(string _filename) { string resultReport=""; //调试信息 Object Nothing =System.Reflection.Missing.Value; //在上传目录下一定要创建一个tempbfile目录用于存储临时文件 string _file="",_path=Path.GetDirectoryName(_filename)+"\\tempbfile\\",_ext=""; _file=Path.GetFileNameWithoutExtension(_filename); _ext=Path.GetExtension(_filename); string _validfilename=Guid.NewGuid().ToString()+_ext; string _lastfile=_path+_validfilename; string _pdfFile = _path + Guid.NewGuid().ToString() + ".pdf"; File.Copy(_filename,_lastfile,true); if(!File.Exists(_lastfile)) { resultReport += "create " + _lastfile + " fail.<br>"; return ""; } //取得Word文件保存路径 object filename=_lastfile; //创建一个名为WordApp的组件对象 Word.Application WordApp=new Word.Application(); //创建一个名为WordDoc的文档对象 WordApp.DisplayAlerts=Word.WdAlertLevel.wdAlertsNone; Word.Document WordDoc=WordApp.Documents.Open(ref filename,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing); WordDoc.SpellingChecked = false; WordDoc.ShowSpellingErrors = false; string pdfFilename = ""; //导出到pdf文件 WordDoc.ExportAsFixedFormat(_pdfFile, Microsoft.Office.Interop.Word.WdExportFormat.wdExportFormatPDF,false,Word.WdExportOptimizeFor.wdExportOptimizeForPrint, wdExportRange,pagefrom,pageto,Word.WdExportItem.wdExportDocumentContent,false,true,Word.WdExportCreateBookmarks.wdExportCreateNoBookmarks, true,true,false, ref Nothing); if (File.Exists(_pdfFile)) { pdfFilename = _pdfFile; } WordDoc.Close(ref Nothing, ref Nothing, ref Nothing); //关闭WordApp组件对象 WordApp.Quit(ref Nothing, ref Nothing, ref Nothing); return pdfFilename; }
Pdf 转批量 Jpeg
生成pdf文件后,我们需要将其转化到指定目录下,批量生成JPEG图片,以备客户端JS进行调用。
方法介绍:
public void PdfToImage(string pdfInputPath, string imageOutputPath,string imageName)
//参数1:PDF文件路径,参数2:输出图片的路径,参数3:图片文件名的前缀,比如输入Img,则会输出Img_001.jpg、Img_002.jpg。。。以此类推。
示例代码如下:
//参数1:PDF文件路径,参数2:输出图片的路径,参数3:图片文件名的前缀,比如输入Img,则会输出Img_001.jpg、Img_002.jpg以此类推 public void PdfToImage(string pdfInputPath, string imageOutputPath,string imageName) { // PdfRenderFlags.Annotations 改成 PdfRenderFlags.CorrectFromDpi DPI值设置成600 即可高清图像 if (Directory.Exists(imageOutputPath) == false) { Directory.CreateDirectory(imageOutputPath); } var pdf = PdfiumViewer.PdfDocument.Load(pdfInputPath); var pdfpage = pdf.PageCount; var pagesizes = pdf.PageSizes; if (pdfpage == 0) { pdf.Dispose(); return; } var document = PdfiumViewer.PdfDocument.Load(pdfInputPath); for (int i = 1; i <= pdfpage; i++) { Size size = new Size(); size.Height = (int)pagesizes[(i - 1)].Height; size.Width = (int)pagesizes[(i - 1)].Width; //可以把".jpg"写成其他形式 string tmpfile = imageOutputPath + imageName + "_" + i.ToString().PadLeft(3, '0') + ".jpg"; var stream = new FileStream(tmpfile, FileMode.Create); var image = document.Render(i - 1, size.Width, size.Height, 120, 120, PdfRenderFlags.CorrectFromDpi); image.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg); stream.Close(); } document.Dispose(); pdf.Dispose(); }
Jpeg 转为电子书
根据 turn.js 的格式要求,我们在服务端 Page_Load 事件里生成一个 ViewState,直接输出到客户端,ViewState["result"] 是我们要输出的变量,我们对指定的 jpgTmpPath 变量目录进行遍历,符合jpeg或jpg扩展名的文件则进行记录。
服务端示例代码如下:
protected void Page_Load(object sender, EventArgs e) { ViewState["result"] = ""; //关键的viewsate,用于存储JPEG地址数组格式 string _cid=Request.QueryString["cid"]; if ( _cid!= null) { string result = ""; string jpgTmpPath = Request.PhysicalApplicationPath + "\\ebook\\" + _cid + "\\"; if (Directory.Exists(jpgTmpPath)) { string[] allfs = System.IO.Directory.GetFiles(jpgTmpPath); for (int i = 0; i < allfs.Length; i++) { string jpgfile = allfs[i].ToLower(); string filename = System.IO.Path.GetFileName(jpgfile); if (jpgfile.IndexOf(".jpg") == -1 && jpgfile.IndexOf(".jpeg") == -1) { continue; } result += "\"../../ebook/" + _cid + "/" + filename + "\",\r\n"; } ViewState["result"] = result; } } if (ViewState["result"].ToString() == "") { Response.Write("没有预览资源"); Response.End(); } }
中间UI代码引用示例:
<head runat="server"> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> <meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="Cache-Control" content="no-cache,no-store,must-revalidate"/> <meta http-equiv="Expires" content="0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/> <meta name="format-detection" content="telephone=no"> <meta name="apple-mobile-web-app-capable" content="yes"/> <title>电子期刊预览</title> <link rel="stylesheet" type="text/css" href="css/basic.css"/> <script type="text/javascript" src="js/jquery.js"></script> <script type="text/javascript" src="js/modernizr.2.5.3.min.js"></script> </head> <div class="shade"> <div class="sk-fading-circle"> <div class="sk-circle1 sk-circle"></div> <div class="sk-circle2 sk-circle"></div> <div class="sk-circle3 sk-circle"></div> <div class="sk-circle4 sk-circle"></div> <div class="sk-circle5 sk-circle"></div> <div class="sk-circle6 sk-circle"></div> <div class="sk-circle7 sk-circle"></div> <div class="sk-circle8 sk-circle"></div> <div class="sk-circle9 sk-circle"></div> <div class="sk-circle10 sk-circle"></div> <div class="sk-circle11 sk-circle"></div> <div class="sk-circle12 sk-circle"></div> </div> <div class="number"></div> </div> <div class="flipbook-viewport" style="display:none;"> <div class="previousPage"></div> <div class="nextPage"></div> <div class="return"></div> <img class="btnImg" src="./image/btn.gif" style="display: none"/> <div class="container"> <div class="flipbook"> </div> <div class="pagenumber"> </div> </div> </div>
客户端脚本:
在客户端我们接收来自 ViewState["result"] 的变量值,实现电子书的效果:
<script type="text/javascript"> var loading_img_url = [ <%=ViewState["result"]%> ]; </script> <script type="text/javascript" src="js/main.js"></script> <script> //自定义弹出层 (function ($) { //ios confirm box jQuery.fn.confirm = function (title, option, okCall, cancelCall) { var defaults = { title: null, //what text cancelText: '取消', //the cancel btn text okText: '确定' //the ok btn text }; if (undefined === option) { option = {}; } if ('function' != typeof okCall) { okCall = $.noop; } if ('function' != typeof cancelCall) { cancelCall = $.noop; } var o = $.extend(defaults, option, { title: title, okCall: okCall, cancelCall: cancelCall }); var $dom = $(this); var dom = $('<div class="g-plugin-confirm">'); var dom1 = $('<div>').appendTo(dom); var dom_content = $('<div>').html(o.title).appendTo(dom1); var dom_btn = $('<div>').appendTo(dom1); var btn_cancel = $('<a href="#"></a>').html(o.cancelText).appendTo(dom_btn); var btn_ok = $('<a href="#"></a>').html(o.okText).appendTo(dom_btn); btn_cancel.on('click', function (e) { o.cancelCall(); dom.remove(); e.preventDefault(); }); btn_ok.on('click', function (e) { o.okCall(); dom.remove(); e.preventDefault(); }); dom.appendTo($('body')); return $dom; }; })(jQuery); if ($(window).width() > 1024 && $(window).height() > 700) { //上一页 $(".previousPage").bind("click", function () { var pageCount = $(".flipbook").turn("pages"); //总页数 var currentPage = $(".flipbook").turn("page"); //当前页 if (currentPage > 2) { $(".flipbook").turn('page', currentPage - 2); } else if (currentPage == 2) { $(".flipbook").turn('page', currentPage - 1); } }); // 下一页 $(".nextPage").bind("click", function () { var pageCount = $(".flipbook").turn("pages"); //总页数 var currentPage = $(".flipbook").turn("page"); //当前页 if (currentPage < pageCount - 1) { $(".flipbook").turn('page', currentPage + 2); } else if (currentPage == pageCount - 1) { $(".flipbook").turn('page', currentPage + 1); } }); } else { //上一页 $(".previousPage").bind("click", function () { var pageCount = $(".flipbook").turn("pages"); //总页数 var currentPage = $(".flipbook").turn("page"); //当前页 if (currentPage >= 2) { $(".flipbook").turn('page', currentPage - 1); } else { } }); // 下一页 $(".nextPage").bind("click", function () { var pageCount = $(".flipbook").turn("pages"); //总页数 var currentPage = $(".flipbook").turn("page"); //当前页 if (currentPage <= pageCount) { $(".flipbook").turn('page', currentPage + 1); } else { } }); } //返回到目录页 $(".return").bind("click", function () { $(document).confirm('您确定要返回首页吗?', {}, function () { $(".flipbook").turn('page', 1); //跳转页数 }, function () { }); }); function gotopage(pageindex) { $(".flipbook").turn('page',pageindex); } </script>
实现效果演示
小结
以上提供的代码仅供参考,turn.js 我花了一些时间进行了改造,我们也可以根据自己的需要对样式、控制进行改造。其它的一些细节我们可以进一步调整,如图片生成质量、权限控制等。
另外,还可以实现下载、评价、点赞、收藏等其它功能,这里就不再一一介绍。
以上就是自己的一些分享,时间仓促,不妥之处还请大家批评指正!