最近做项目用户有个需求,要求在线预览上传的pdf文件。遂上网寻找解决路线,一阵狂搜,得到答案swftools+flexPaper.
swftools:用来将pdf文件转换成swf文件http://www.swftools.org/download.html
flexPaper:用来在线预览swf文件http://flexpaper.devaldi.com/download/ 下载Download Web Server Package版本。
写了一个工具类用来调用本地应用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
package
com.hyges.rsac.utils;
import
java.io.BufferedReader;
import
java.io.File;
import
java.io.InputStream;
import
java.io.InputStreamReader;
import
org.apache.commons.logging.Log;
import
org.apache.commons.logging.LogFactory;
public
class
NativeApplicationInvocationHandler {
public
static
Log logger = LogFactory.getLog(NativeApplicationInvocationHandler.
class
);
private
static
String[] windows =
new
String[] {
"cmd"
,
"/c"
};
/**
* 暴露给用户的调用函数
*
* @param workDir
* 工作目录
* @param command
* 执行命令
*/
public
static
void
invocate(String workDir, String... command) {
if
(isWinPlatform()) {
// windows平台
boolean
cmd =
false
;
boolean
c =
false
;
// 判断命令里面是否包含cmd /c
for
(String item : command) {
if
(item.contains(
"cmd"
)) {
cmd =
true
;
}
if
(item.contains(
"/c"
)) {
c =
true
;
}
}
if
(cmd && c) {
invocateForInner(workDir, command);
}
else
{
String[] newCommand =
new
String[command.length + windows.length];
System.arraycopy(windows,
0
, newCommand,
0
, windows.length);
System.arraycopy(command,
0
, newCommand, windows.length, command.length);
invocateForInner(workDir, newCommand);
}
}
else
{
// linux平台
invocateForInner(workDir, command);
}
}
/**
* 判断宿主平台
*
* @return
*/
private
static
boolean
isWinPlatform() {
String platform = System.getProperty(
"os.name"
);
logger.info(
"本地应用调用:宿主平台是"
+ platform);
return
platform.toLowerCase().indexOf(
"windows"
) > -
1
?
true
:
false
;
}
/**
* 记录调用错误
*
* @param process
* @throws IOException
*/
/*
* private static void loggingError(Process process) throws IOException {
* BufferedInputStream bis = new
* BufferedInputStream(process.getErrorStream()); int tmp; ArrayList<Byte>
* list = new ArrayList<Byte>(); while ((tmp = bis.read()) != -1) {
* list.add((byte) tmp); } Object[] tmpA = list.toArray(); byte[] result =
* new byte[tmpA.length]; for (int i = 0; i < tmpA.length; i++) { result[i]
* = (Byte) tmpA[i]; } bis.close(); logger.error("本地应用调用:调用出现错误:" + new
* String(result, Charset.defaultCharset())); }
*/
/**
* 类内部的调用函数
*
* @param workDir
* @param command
*/
private
static
void
invocateForInner(String workDir, String... command) {
Process process;
ProcessBuilder pb;
pb =
new
ProcessBuilder(command);
pb.directory(
new
File(workDir));
pb.redirectErrorStream(
true
);
// 合并stdout和stderr
try
{
logger.info(
"本地应用调用:调用开始"
);
process = pb.start();
InputStream is = process.getInputStream();
BufferedReader br =
new
BufferedReader(
new
InputStreamReader(is));
StringBuilder sb =
new
StringBuilder();
String line;
// 清空process.getInputStream()的缓冲区,以免造成程序阻塞。
while
((line = br.readLine()) !=
null
) {
sb.append(line);
}
br.close();
if
(process.waitFor() !=
0
|| process.exitValue() !=
0
) {
logger.error(
"本地应用调用:调用出现错误:"
+ sb.toString());
}
else
{
process.destroy();
logger.info(
"本地应用调用:调用成功结束"
);
}
}
catch
(Exception e) {
logger.error(
"程序调用出现错误"
, e);
}
}
}
|
在写这个工具类时遇到了两个问题:
第一个问题,在转换大的pdf文件时,运行很慢不能成功转换。上网查了一下原因,有的说是Process类里面I/O流产生阻塞,导致线程不能继续执行,遂查看api
By default, the created subprocess does not have its own terminal or console. All its standard I/O (i.e. stdin, stdout, stderr) operations will be redirected to the parent process, where they can be accessed via the streams obtained using the methods , , and . The parent process uses these streams to feed input to and get output from the subprocess. Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock.
大致意思,默认情况下被创建的子进程没有终端或控制台。所有的标准I/O流都被重定向到了父线程,可以通过 , 和.方法获得这些流。父进程通过这些流与子进程交互。有一些宿主平台只提供了大小有限制的缓存,来缓存这些输入输出流。当缓冲区填满了,就不能及时写入输入流或读出子进程的输出流,这可能导致子进程阻塞,甚至死锁。
原因找到了,就是因为程序在被调用执行时,产生了大量的输出流,而笔者又没有及时读取子进程的输出,所以导致子进程被阻塞了。(以上工具类已经读取了子进程的输出流)
这里要解释一下和.的区别
以下属于errorStream
一下属于inputStream
这下读者该能够区分什么时候调用哪个方法了吧。
第二个问题:
用flexPaper查看被转换的swf文件时,右上角总是有个小圆圈再转,并且显示已经100%了,但就是打不开。问题是flash版本问题在调用pdf2swf.exe时加上版本信息就好了
1
2
|
invocate(
"D:\\Program Files (x86)\\SWFTools"
,
"cmd"
,
"/c"
,
"pdf2swf.exe"
,
"-t"
,
"d:\\hibernate_reference.pdf"
,
"-s"
,
"flashversion=9"
,
"-o"
,
"d:\\hibernate_reference.swf"
);
|
这样问题顺利解决。
以下是显示页面主要代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
<script type=
"text/javascript"
src=
"resources/component/flexPaper/js/flexpaper.js"
></script>
<script type=
"text/javascript"
src=
"resources/component/flexPaper/js/flexpaper_handlers.js"
></script>
<div
class
=
"wrap mt20"
>
<div
class
=
"website"
>
当前位置:<a href=
"/"
>首页</a> > <a href=
"publication/list"
>协会刊物</a>>${name }
</div>
<div
class
=
"periodical-cont"
>
<input name=
"swfPath"
value=
"${path}"
hidden=
"true"
id=
"swfPath"
/>
<div id=
"documentViewer"
class
=
"flexpaper_viewer"
style=
"width: 770px; height: 500px"
></div>
<script type=
"text/javascript"
>
var startDocument =
"Paper"
;
var swfPath = $(
"#swfPath"
).val();
$(
'#documentViewer'
).FlexPaperViewer({
config : {
SWFFile : swfPath,
Scale :
0.6
,
ZoomTransition :
'easeOut'
,
ZoomTime :
0.5
,
ZoomInterval :
0.2
,
FitPageOnLoad :
true
,
FitWidthOnLoad :
false
,
FullScreenAsMaxWindow :
false
,
ProgressiveLoading :
false
,
MinZoomSize :
0.2
,
MaxZoomSize :
5
,
SearchMatchAll :
false
,
InitViewMode :
'Portrait'
,
RenderingOrder :
'flash'
,
StartAtPage :
''
,
ViewModeToolsVisible :
true
,
ZoomToolsVisible :
true
,
NavToolsVisible :
true
,
CursorToolsVisible :
true
,
SearchToolsVisible :
true
,
WMode :
'window'
,
localeChain :
'zh_CN'
}
});
</script>
</div>
|