zoukankan      html  css  js  c++  java
  • ASP.Net实现将Word转换PDF格式

     前言:由于一个客户的项目中需要将WORD文档转换成PDF格式,故写了本篇实站教程

      需求分析:客户的项目以B/S结构为主,提供一个WORD文件在后台自动转换成PDF,经过实际测试,如果该篇WORD文档有100多页的话,转换需要20分钟左右的时间(环境:CPU是奔腾M 1.6G,512M内存),整个CPU的占用率近乎95%~100%,此结果告诉客户以后,客户提议:到客户下班后,自动转换PDF,同时如果使用人确认要查看该PDF文档,如果没有转换,提供给客户选择,是现在转换成PDF,还是由服务器在客户下班后,自动转换。

      项目功能:按需求分析要写两个功能

      第一为:B/S结构后台转换,要提交给客户选择

      第二为:Windows服务自动转换WORD文档到PDF

      这两个分类:核心的转换程序都是采用线程的方式执行,只不过第一个功能是针对一个WORD文件,第二个功能针对所有未转换的WORD文档.

      分析到现在:我们开始实战转换了!

      一:必备工具

      安装必须的工具MS VS.Net2003,MS Office2003,Adobe Acrobat 7.0 Professional,postscript.exe,gs811w32.exe

      MS VS.Net2003的安装不说明

      MS Office2003的安装不说明

      Adobe Acrobat 7.0 Professional安装说明

      运行setup.exe文件,出现输入序列号,就运行注册机,用鼠标在第一行刷下就可以看见序列号,复制粘贴到Adobe Acrobat 7.0 Professional安装程序对话框,安装到最后出现注册时,点击PHONE...将安装程序中显示的第二行序列号(第一行是刚才注册机生成的序列号)复制粘贴到注册机的第二行,点击右边的按钮,再用鼠标刷第三行授权号就出来了,将其复制粘贴到安装程序的最后一行,完成安装注册!

      postscript.exe默认安装就可以了,它是一个PDF转换时所需要的脚本

      gs811w32.exe默认安装就可以,它其实是个PDF虚拟打印机的驱动

      二:配置虚拟打印机

      进入Windows的控制面板,进入打印机,点击"添加打印机"图标.在安装对话框上"按一步",出现选择打印机时,在制造商一栏中选择"Generic",在打印机一栏中,选择"MS Publisher Color Printer",然后一路按下一步,知道安装结束.

      三:开始写第一个程序(脚本程序)

      为什么要使用脚本程序进行转换呢,其实实际测试过程中,使用PDF Distiller的对象引用到C#后,转换成功,但整个PDF Distiller对象不能释放,第二次再转换时,就发生了错误,故此处使用脚本程序实现转换.这样我们只要在C#的程序中调用脚本程序就可以实现WORD到PDF的转换。

      宿主脚本文件名:ConvertDoc2PDF.js

      脚本文件内容:

    var files = WScript.Arguments;
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    var word = new ActiveXObject("Word.Application");
    var PDF = new ActiveXObject("PDFDistiller.PDFDistiller.1");
    word.ActivePrinter = "MS Publisher Color Printer";

    //files(0) 为WORD文档文件名
    //files(1) 为,转换后需要保存的路径
    //调用fso.GetBaseName(files(0))后,为无路径,无扩展名,的文件名
    //files.length为文件参数的个数,使用循环可以支持多个WORD文档的转换

    var docfile = files(0);
    var psfile = files(1) + fso.GetBaseName(files(0)) + ".ps";
    var pdffile = files(1) + fso.GetBaseName(files(0)) + ".pdf";
    var logfile = files(1) + fso.GetBaseName(files(0)) + ".log";

    try{
    var doc = word.Documents.Open(docfile);
    //WORD文件转成PS文件;
    word.PrintOut(false, false, 0, psfile);
    doc.Close(0);

    //PS文件转成PDF文件;
    PDF.FileToPDF(psfile,pdffile,"");

    fso.GetFile(psfile).Delete();//删除PS脚本文件
    fso.GetFile(logfile).Delete();//删除转换的日志文件

    word.Quit();
    WScript.Echo("isuccess");//成功
    WScript.Quit(0);
    }
    catch(x)
    {
    word.Quit();
    WScript.Echo("isfail");//失败
    WScript.Quit(0);
    }

      然后测试该脚本程序

      启动MS-DOS,输入如下命令:

    c:\>cscript //nologo c:\ConvertDoc2PDF.js c:\test.doc c:\

      说明:

      运行成功后将看到test.pdf文档了

      c:\test.doc参数对应的是脚本程序中的files(0)

      c:\参数对应的是脚本程序中的files(1)

      你可以安照该脚本改写成,支持多个参数,使用FOR循环,一次转换多个WORD文档,此处没有使用多个文件转换功能,是考虑到,该段脚本放在C#的线程中执行,这样一来也可以转换多个WORD文档.

      四:使用C#调用ConvertDoc2PDF.js脚本

      新建一个C#的WINDOWS应用程序,添加一个按钮button1

      添加一个函数,函数名StartConvertPDF

    public void StartConvertPDF()
    {
     Process proc = new Process();
     proc.StartInfo.FileName = "cmd.exe";
     proc.StartInfo.WorkingDirectory = @"c:\";
     proc.StartInfo.CreateNoWindow = true;
     proc.StartInfo.UseShellExecute = false;
     proc.StartInfo.RedirectStandardInput = true; //输入重定向

     proc.Start();
     proc.StandardInput.WriteLine(@"cscript //nologo c:\ConvertDoc2PDF.js c:\test.doc c:\");
     proc.StandardInput.WriteLine("exit");
     proc.WaitForExit();
    }

      然后在按钮的CLICK事件中添加调用线程的代码

    private void button1_Click(object sender, System.EventArgs e)
    {
    //定义线程序
    Thread thConvert = new Thread(new ThreadStart(StartConvertData));
    thConvert.Start();
    }

      注意:在测试上面的C#程序时,必须添加如下命名空间

    using System.Diagnostics;
    using System.Threading;

      五:健壮的C#调用代码(实际考虑,可放在B/S系统中)

      完成第4步的C#测试后,细心的读者,可能看到一点问题,那就是如何得到脚本运行后输出的结果,如何给线程中调用的StartConvertData方法传递参数

      1:传递参数,此话说来也可用一篇教程告诉大家线程中方法如何来传递参数,现在就讲一个方案,此种方案很多,我采用一个类,初始化这个类,然后调用该类的方法作为线程执行的方法

      2:得到脚本的输出结果,使用Process对象的输出重定向,就是说改变输出方向,使脚本不输出到控制台(MS-DOS窗口),而是重定向输出到C#程序中,并采用线程的异步回调方法,显示脚本运行结果。

      添加一个新类,类名为ToPdf

    using System;
    using System.Diagnostics;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;

    namespace Doc2Pdf
    {
    public class ToPdf
    {
    private string strWord = "";//此处的WORD文件不含路径
    private string sPath = "";
    public string sExecResult = "";
    public bool bSuccess = false;

    public ToPdf(string sParamWord,string sParamPath)
    {
    strWord = sParamWord;
    sPath = sParamPath;
    }

    public void StartConvertPDF()
    {
    Process proc = new Process();
       proc.StartInfo.FileName = "cmd.exe";
       proc.StartInfo.WorkingDirectory = sPath;
       proc.StartInfo.CreateNoWindow = true;
       proc.StartInfo.UseShellExecute = false;
       proc.StartInfo.RedirectStandardInput = true;//标准输入重定向
    proc.StartInfo.RedirectStandardOutput = true;//标准输出重定向

       proc.Start();
    proc.StandardInput.WriteLine("cscript //nologo "+sPath+"ConvertDoc2PDF.js "+sPath+strWord+ " "+sPath);
    proc.StandardInput.WriteLine("exit");
    sExecResult = proc.StandardOutput.ReadToEnd();//返回脚本执行的结果
    proc.WaitForExit();
    proc.Close();

    }

    public void EndConvertPDF(System.IAsyncResult ar)//ar参数必须写,是线程执行完成后的回调函数
    {
    if(sExecResult.IndexOf("isuccess")!=-1)bSuccess=true;
    else if(sExecResult.IndexOf("isfail")!=-1)bSuccess=false;
    //如果放在B/S系统,你可以在此处写数据库,是成功还是失败,并用一个WEBService程序不断检查数据库,此WEBService程序不放在该回调用函数中
    //如果放在C/S系统,回调函数可以不放在类中,以便在窗体程序中调用结果
    }
    }
    }

      改写原来的button1_Click事件中的代码

    private void button1_Click(object sender, System.EventArgs e)
    {
    ToPdf my2Pdf = new ToPdf("test.doc","c:\\");
    ThreadStart thStartConvert = new ThreadStart(my2Pdf.StartConvertPDF); //开始异步调用线程
    thStartConvert.BeginInvoke(new AsyncCallback(my2Pdf.EndConvertPDF),null);//设置异步线程的回调函数

    //如果需要转换多个WORD,你可以用循环
    //如果是B/S系统,可以将本段代码放在ASPX中,并结合客户端的无刷新显示数据的技术,不断访问WEBService程序,以确定PDF是否转换成功或失败
    }

      六:编写更加健壮的C#调用代码(实际考虑,可放在WINDOWS的服务程序中)

      实际使用时,由于转化PDF时CPU的占用率很高,考虑只在同一时间转换一篇WORD文档,放弃异步线程的回调函数的使用,考虑一个WINDOWS的服务程序。

      写一个函数CheckData2Convert(),不断的检查没有转换的WORD文档,并使用循环调用ToPdf类中执行转换方法StartConvertPDF

    //以下给出,泛代码,用户按照自己的需求,填写完整即可
    //bool bStart为全局变量,控制循环的进入与退出
    //例:18:30开始检查并转换,那么18:30时,bStart=true;并启动转换线程
    //6:30停止转换线程,bStart=fasle;

    private void CheckData2Convert()
    {
    //检查指定目录下的没有转换的WORD文档,你同样可以检查数据库中记录的没有转换的WORD文档
    string sPath = System.Threading.Thread.GetDomain().BaseDirectory; //当前的路径
    while(bStart)
    {
    int iFileCount = CheckWord(); //CheckWord为一个方法,检查当前没有转换的WORD文档,返回没有转换的文件数,该方法的代码由读者自己编写
    for(int i=0;i<iFileCount;i++)
    {
    string sWord = GetWordFileName(i) //GetWordFileName为一个方法,返回一个不带路径的WORD文件名,该方法的代码由读者自己编写
    //ToPdf类中的StartConvertPDF()方法使用的是不带路径的WORD文件名
    ToPdf my2Pdf = new ToPdf(sWord ,sPath);
    my2Pdf.StartConvertPDF();

    if(my2Pdf.sExecResult.IndexOf("isuccess")!=-1)
    {
    //成功,写日志,或回写数据库
    }
    else if(my2Pdf.sExecResult.IndexOf("isfail")!=-1)
    {
    //失败,写日志,或回写数据库
    }

    }

    if(!bStart)break;
    Thread.Sleep(1000);
    }
    }

      然后在服务的开始事件中,启动线程

    protected override void OnStart(string[] args)
    {
    //可以使用一个开始定时器,检查是否到开始时间,时间一到,就开始执行线程,此处的开始执行线程可以放在开始定时事件中
    //可以使用一个结束定时器,检查是否到结束时间,时间一到,就结束线程,结束线程的代码可以放在结束定时事件中
    //注意:应该使用组件中的定时器,而不是Windows的FORMS中的定时器
    //该定时器的类名为System.Timers.Timer,千万别搞错,不然执行不会正常的
    bStart = true;
    Thread thConvert = new Thread(new ThreadStart(StartConvertData));
    thConvert.Start();
    }

      然后在服务的结束事件中,设置停止线程的标识bStart= false

    protected override void OnStop()
    {
    bStart = false;
    //为何次处不停止线程呢,因为考虑到,现在线程正在转换WORD文档,但没有结束,所以只设置停止标识,转换完成后,线程也执行结束了.
    }

      结束语:

      Adobe Acrobat 7.0 Professional,postscript.exe,gs811w32.exe这三个文件可以在itbaby.jss.cn下载,都包含在同一个RAR的压缩文件中了。
     

  • 相关阅读:
    又玩起了“数独”
    WebService应用:音乐站图片上传
    大家都来DIY自己的Blog啦
    CSS导圆角,不过这个代码没有怎么看懂,与一般的HTML是不同
    网站PR值
    CommunityServer2.0何去何从?
    网络最经典命令行
    炎热八月,小心"落雪"
    Topology activation failed. Each partition must have at least one index component from the previous topology in the new topology, in the same host.
    SharePoint 2013服务器场设计的一些链接
  • 原文地址:https://www.cnblogs.com/flyfish/p/381865.html
Copyright © 2011-2022 走看看