惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Project Zero
Project Zero
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Scott Helme
Scott Helme
Know Your Adversary
Know Your Adversary
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
WordPress大学
WordPress大学
AWS News Blog
AWS News Blog
小众软件
小众软件
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Jina AI
Jina AI
AI
AI
美团技术团队
人人都是产品经理
人人都是产品经理
S
Secure Thoughts
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
宝玉的分享
宝玉的分享
Security Latest
Security Latest
P
Privacy & Cybersecurity Law Blog
C
Cisco Blogs
大猫的无限游戏
大猫的无限游戏
Google Online Security Blog
Google Online Security Blog
L
LINUX DO - 最新话题
罗磊的独立博客
Recent Announcements
Recent Announcements
H
Hacker News: Front Page
博客园 - 【当耐特】
K
Kaspersky official blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
SecWiki News
SecWiki News
Schneier on Security
Schneier on Security
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Apple Machine Learning Research
Apple Machine Learning Research
F
Full Disclosure
Google DeepMind News
Google DeepMind News
V
V2EX
博客园 - 聂微东
量子位
云风的 BLOG
云风的 BLOG
C
Check Point Blog
J
Java Code Geeks
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
W
WeLiveSecurity
Engineering at Meta
Engineering at Meta
V2EX - 技术
V2EX - 技术
Vercel News
Vercel News
L
LINUX DO - 热门话题
T
The Exploit Database - CXSecurity.com
L
Lohrmann on Cybersecurity
The GitHub Blog
The GitHub Blog

博客园 - Candyxiaoqiang

使用 ADO.NET 访问 Oracle 9i 存储过程(转) 使用 ADO.NET 和 Oracle 进行高级数据访问 (转自) HttpRequest, HttpRuntime, AppDomain and friends() 在showModalDialog窗体里,单击服务端Button产生新页面解决方法 - Candyxiaoqiang - 博客园 asp.net按钮 button的onclick事件 与oncommand 事件的区别 汇总 SQL 连接字符串的说明(转) JavaScript相关资料( 收集中.......) - Candyxiaoqiang - 博客园 Visual Studio常用小技巧 觉得不错 转一下 Web开发中用的工具(不断更新) C#实现Web文件上传的两种方法 (转) document,event javascript中常用对象介绍 带中文说明的 (转) 用C#实现生成PDF文档和将WORD转换为PDF (转自海东的技术资料) C# 拆分word(根据标题或书签拆分) (转自zrx401558287) 完成类似QQ邮箱中‘HTML方式查看’功能查看Office文件 (转自zellzhang) 如何:将文档发送到打印机(转) 利用.net替换Word的内容(从数据库中取数据来替换word里面的书签)(转) 在IIS上启用Gzip压缩(HTTP压缩) (转自子秋的博客) 如何采用Local方式连接到ArcGIS Server(转) VS2005 直接调试网页不能显示
ASP.Net实现将Word转换PDF格式 (转自:思绪随风~~~)
Candyxiaoqiang · 2009-05-21 · via 博客园 - Candyxiaoqiang

作者:佚名  来源:动态网站制作指南  时间:2006-3-28

 前言:由于一个客户的项目中需要将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的压缩文件中了。

   itbaby.jss.cn是动态域名,主机在作者家里,如果网站不能访问,说明电脑没有开,请稍后几天再试。