关于java生成复杂的word

JAVA生成复杂的WORD其实并不容易,以往项目中,更多的做法都是通过前台IE的WORD控件进行复杂的生成操作之后,再保存至服务器。如果需要纯JAVA来生成复杂的word,搜索了很多例子,这个使用freemarker的方法最靠谱。

    具体的思路:

  • 1.将处理好的word模板,另存为word xml格式。并在其中添加参数例如:${export}。
  • 2.将xml文档修改完成后,保存并修改后缀名为“****.ftl”。
  • 3.编写java类,实现对模板的赋值以及生成流,用于导出。

首先我建立了一个包含表格的word,另存为word xml格式,之后替换关键位置字符为标签${mc},${ly},${rq}等,以下为具体XML信息,保存为test.ftl。

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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument xmlns:aml="http://schemas.microsoft.com/aml/2001/core" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml" xmlns:wx="http://schemas.microsoft.com/office/word/2003/auxHint" xmlns:wsp="http://schemas.microsoft.com/office/word/2003/wordml/sp2" xmlns:sl="http://schemas.microsoft.com/schemaLibrary/2003/core" w:macrosPresent="no" w:embeddedObjPresent="no" w:ocxPresent="no" xml:space="preserve">
	<w:ignoreSubtree w:val="http://schemas.microsoft.com/office/word/2003/wordml/sp2"/>
	<o:DocumentProperties>
		<o:Author>LL19</o:Author>
		<o:LastAuthor>LL19</o:LastAuthor>
		<o:Revision>2</o:Revision>
		<o:TotalTime>3</o:TotalTime>
		<o:Created>2012-03-11T17:08:00Z</o:Created>
		<o:LastSaved>2012-03-11T17:08:00Z</o:LastSaved>
		<o:Pages>1</o:Pages>
		<o:Words>9</o:Words>
		<o:Characters>53</o:Characters>
		<o:Company>LL19.com</o:Company>
		<o:Lines>1</o:Lines>
		<o:Paragraphs>1</o:Paragraphs>
		<o:CharactersWithSpaces>61</o:CharactersWithSpaces>
		<o:Version>12</o:Version>
	</o:DocumentProperties>
	<w:fonts>
		<w:defaultFonts w:ascii="Calibri" w:fareast="宋体" w:h-ansi="Calibri" w:cs="Times New Roman"/>
		<w:font w:name="Times New Roman">
			<w:panose-1 w:val="02020603050405020304"/>
			<w:charset w:val="00"/>
			<w:family w:val="Roman"/>
			<w:pitch w:val="variable"/>
			<w:sig w:usb-0="20002A87" w:usb-1="80000000" w:usb-2="00000008" w:usb-3="00000000" w:csb-0="000001FF" w:csb-1="00000000"/>
		</w:font>
		<w:font w:name="宋体">
			<w:altName w:val="SimSun"/>
			<w:panose-1 w:val="02010600030101010101"/>
			<w:charset w:val="86"/>
			<w:family w:val="auto"/>
			<w:pitch w:val="variable"/>
			<w:sig w:usb-0="00000003" w:usb-1="080E0000" w:usb-2="00000010" w:usb-3="00000000" w:csb-0="00040001" w:csb-1="00000000"/>
		</w:font>
		<w:font w:name="Cambria Math">
			<w:panose-1 w:val="02040503050406030204"/>
			<w:charset w:val="00"/>
			<w:family w:val="Roman"/>
			<w:pitch w:val="variable"/>
			<w:sig w:usb-0="A00002EF" w:usb-1="420020EB" w:usb-2="00000000" w:usb-3="00000000" w:csb-0="0000009F" w:csb-1="00000000"/>
		</w:font>
		<w:font w:name="Calibri">
			<w:panose-1 w:val="020F0502020204030204"/>
			<w:charset w:val="00"/>
			<w:family w:val="Swiss"/>
			<w:pitch w:val="variable"/>
			<w:sig w:usb-0="A00002EF" w:usb-1="4000207B" w:usb-2="00000000" w:usb-3="00000000" w:csb-0="0000009F" w:csb-1="00000000"/>
		</w:font>
		<w:font w:name="@宋体">
			<w:panose-1 w:val="02010600030101010101"/>
			<w:charset w:val="86"/>
			<w:family w:val="auto"/>
			<w:pitch w:val="variable"/>
			<w:sig w:usb-0="00000003" w:usb-1="080E0000" w:usb-2="00000010" w:usb-3="00000000" w:csb-0="00040001" w:csb-1="00000000"/>
		</w:font>
	</w:fonts>
	<w:styles>
		<w:versionOfBuiltInStylenames w:val="7"/>
		<w:latentStyles w:defLockedState="off" w:latentStyleCount="267">
			<w:lsdException w:name="Normal"/>
			<w:lsdException w:name="heading 1"/>
			<w:lsdException w:name="heading 2"/>
			<w:lsdException w:name="heading 3"/>
			<w:lsdException w:name="heading 4"/>
			<w:lsdException w:name="heading 5"/>
			<w:lsdException w:name="heading 6"/>
			<w:lsdException w:name="heading 7"/>
			<w:lsdException w:name="heading 8"/>
			<w:lsdException w:name="heading 9"/>
			<w:lsdException w:name="toc 1"/>
			<w:lsdException w:name="toc 2"/>
			<w:lsdException w:name="toc 3"/>
			<w:lsdException w:name="toc 4"/>
			<w:lsdException w:name="toc 5"/>
			<w:lsdException w:name="toc 6"/>
			<w:lsdException w:name="toc 7"/>
			<w:lsdException w:name="toc 8"/>
			<w:lsdException w:name="toc 9"/>
			<w:lsdException w:name="caption"/>
			<w:lsdException w:name="Title"/>
			<w:lsdException w:name="Default Paragraph Font"/>
			<w:lsdException w:name="Subtitle"/>
			<w:lsdException w:name="Strong"/>
			<w:lsdException w:name="Emphasis"/>
			<w:lsdException w:name="Table Grid"/>
			<w:lsdException w:name="Placeholder Text"/>
			<w:lsdException w:name="No Spacing"/>
			<w:lsdException w:name="Light Shading"/>
			<w:lsdException w:name="Light List"/>
			<w:lsdException w:name="Light Grid"/>
			<w:lsdException w:name="Medium Shading 1"/>
			<w:lsdException w:name="Medium Shading 2"/>
			<w:lsdException w:name="Medium List 1"/>
			<w:lsdException w:name="Medium List 2"/>
			<w:lsdException w:name="Medium Grid 1"/>
			<w:lsdException w:name="Medium Grid 2"/>
			<w:lsdException w:name="Medium Grid 3"/>
			<w:lsdException w:name="Dark List"/>
			<w:lsdException w:name="Colorful Shading"/>
			<w:lsdException w:name="Colorful List"/>
			<w:lsdException w:name="Colorful Grid"/>
			<w:lsdException w:name="Light Shading Accent 1"/>
			<w:lsdException w:name="Light List Accent 1"/>
			<w:lsdException w:name="Light Grid Accent 1"/>
			<w:lsdException w:name="Medium Shading 1 Accent 1"/>
			<w:lsdException w:name="Medium Shading 2 Accent 1"/>
			<w:lsdException w:name="Medium List 1 Accent 1"/>
			<w:lsdException w:name="Revision"/>
			<w:lsdException w:name="List Paragraph"/>
			<w:lsdException w:name="Quote"/>
			<w:lsdException w:name="Intense Quote"/>
			<w:lsdException w:name="Medium List 2 Accent 1"/>
			<w:lsdException w:name="Medium Grid 1 Accent 1"/>
			<w:lsdException w:name="Medium Grid 2 Accent 1"/>
			<w:lsdException w:name="Medium Grid 3 Accent 1"/>
			<w:lsdException w:name="Dark List Accent 1"/>
			<w:lsdException w:name="Colorful Shading Accent 1"/>
			<w:lsdException w:name="Colorful List Accent 1"/>
			<w:lsdException w:name="Colorful Grid Accent 1"/>
			<w:lsdException w:name="Light Shading Accent 2"/>
			<w:lsdException w:name="Light List Accent 2"/>
			<w:lsdException w:name="Light Grid Accent 2"/>
			<w:lsdException w:name="Medium Shading 1 Accent 2"/>
			<w:lsdException w:name="Medium Shading 2 Accent 2"/>
			<w:lsdException w:name="Medium List 1 Accent 2"/>
			<w:lsdException w:name="Medium List 2 Accent 2"/>
			<w:lsdException w:name="Medium Grid 1 Accent 2"/>
			<w:lsdException w:name="Medium Grid 2 Accent 2"/>
			<w:lsdException w:name="Medium Grid 3 Accent 2"/>
			<w:lsdException w:name="Dark List Accent 2"/>
			<w:lsdException w:name="Colorful Shading Accent 2"/>
			<w:lsdException w:name="Colorful List Accent 2"/>
			<w:lsdException w:name="Colorful Grid Accent 2"/>
			<w:lsdException w:name="Light Shading Accent 3"/>
			<w:lsdException w:name="Light List Accent 3"/>
			<w:lsdException w:name="Light Grid Accent 3"/>
			<w:lsdException w:name="Medium Shading 1 Accent 3"/>
			<w:lsdException w:name="Medium Shading 2 Accent 3"/>
			<w:lsdException w:name="Medium List 1 Accent 3"/>
			<w:lsdException w:name="Medium List 2 Accent 3"/>
			<w:lsdException w:name="Medium Grid 1 Accent 3"/>
			<w:lsdException w:name="Medium Grid 2 Accent 3"/>
			<w:lsdException w:name="Medium Grid 3 Accent 3"/>
			<w:lsdException w:name="Dark List Accent 3"/>
			<w:lsdException w:name="Colorful Shading Accent 3"/>
			<w:lsdException w:name="Colorful List Accent 3"/>
			<w:lsdException w:name="Colorful Grid Accent 3"/>
			<w:lsdException w:name="Light Shading Accent 4"/>
			<w:lsdException w:name="Light List Accent 4"/>
			<w:lsdException w:name="Light Grid Accent 4"/>
			<w:lsdException w:name="Medium Shading 1 Accent 4"/>
			<w:lsdException w:name="Medium Shading 2 Accent 4"/>
			<w:lsdException w:name="Medium List 1 Accent 4"/>
			<w:lsdException w:name="Medium List 2 Accent 4"/>
			<w:lsdException w:name="Medium Grid 1 Accent 4"/>
			<w:lsdException w:name="Medium Grid 2 Accent 4"/>
			<w:lsdException w:name="Medium Grid 3 Accent 4"/>
			<w:lsdException w:name="Dark List Accent 4"/>
			<w:lsdException w:name="Colorful Shading Accent 4"/>
			<w:lsdException w:name="Colorful List Accent 4"/>
			<w:lsdException w:name="Colorful Grid Accent 4"/>
			<w:lsdException w:name="Light Shading Accent 5"/>
			<w:lsdException w:name="Light List Accent 5"/>
			<w:lsdException w:name="Light Grid Accent 5"/>
			<w:lsdException w:name="Medium Shading 1 Accent 5"/>
			<w:lsdException w:name="Medium Shading 2 Accent 5"/>
			<w:lsdException w:name="Medium List 1 Accent 5"/>
			<w:lsdException w:name="Medium List 2 Accent 5"/>
			<w:lsdException w:name="Medium Grid 1 Accent 5"/>
			<w:lsdException w:name="Medium Grid 2 Accent 5"/>
			<w:lsdException w:name="Medium Grid 3 Accent 5"/>
			<w:lsdException w:name="Dark List Accent 5"/>
			<w:lsdException w:name="Colorful Shading Accent 5"/>
			<w:lsdException w:name="Colorful List Accent 5"/>
			<w:lsdException w:name="Colorful Grid Accent 5"/>
			<w:lsdException w:name="Light Shading Accent 6"/>
			<w:lsdException w:name="Light List Accent 6"/>
			<w:lsdException w:name="Light Grid Accent 6"/>
			<w:lsdException w:name="Medium Shading 1 Accent 6"/>
			<w:lsdException w:name="Medium Shading 2 Accent 6"/>
			<w:lsdException w:name="Medium List 1 Accent 6"/>
			<w:lsdException w:name="Medium List 2 Accent 6"/>
			<w:lsdException w:name="Medium Grid 1 Accent 6"/>
			<w:lsdException w:name="Medium Grid 2 Accent 6"/>
			<w:lsdException w:name="Medium Grid 3 Accent 6"/>
			<w:lsdException w:name="Dark List Accent 6"/>
			<w:lsdException w:name="Colorful Shading Accent 6"/>
			<w:lsdException w:name="Colorful List Accent 6"/>
			<w:lsdException w:name="Colorful Grid Accent 6"/>
			<w:lsdException w:name="Subtle Emphasis"/>
			<w:lsdException w:name="Intense Emphasis"/>
			<w:lsdException w:name="Subtle Reference"/>
			<w:lsdException w:name="Intense Reference"/>
			<w:lsdException w:name="Book Title"/>
			<w:lsdException w:name="Bibliography"/>
			<w:lsdException w:name="TOC Heading"/>
		</w:latentStyles>
		<w:style w:type="paragraph" w:default="on" w:styleId="a">
			<w:name w:val="Normal"/>
			<wx:uiName wx:val="正文"/>
			<w:pPr>
				<w:widowControl w:val="off"/>
				<w:jc w:val="both"/>
			</w:pPr>
			<w:rPr>
				<wx:font wx:val="Calibri"/>
				<w:kern w:val="2"/>
				<w:sz w:val="21"/>
				<w:sz-cs w:val="22"/>
				<w:lang w:val="EN-US" w:fareast="ZH-CN" w:bidi="AR-SA"/>
			</w:rPr>
		</w:style>
		<w:style w:type="character" w:default="on" w:styleId="a0">
			<w:name w:val="Default Paragraph Font"/>
			<wx:uiName wx:val="默认段落字体"/>
		</w:style>
		<w:style w:type="table" w:default="on" w:styleId="a1">
			<w:name w:val="Normal Table"/>
			<wx:uiName wx:val="普通表格"/>
			<w:rPr>
				<wx:font wx:val="Calibri"/>
				<w:lang w:val="EN-US" w:fareast="ZH-CN" w:bidi="AR-SA"/>
			</w:rPr>
			<w:tblPr>
				<w:tblInd w:w="0" w:type="dxa"/>
				<w:tblCellMar>
					<w:top w:w="0" w:type="dxa"/>
					<w:left w:w="108" w:type="dxa"/>
					<w:bottom w:w="0" w:type="dxa"/>
					<w:right w:w="108" w:type="dxa"/>
				</w:tblCellMar>
			</w:tblPr>
		</w:style>
		<w:style w:type="list" w:default="on" w:styleId="a2">
			<w:name w:val="No List"/>
			<wx:uiName wx:val="无列表"/>
		</w:style>
		<w:style w:type="paragraph" w:styleId="a3">
			<w:name w:val="header"/>
			<wx:uiName wx:val="页眉"/>
			<w:basedOn w:val="a"/>
			<w:link w:val="Char"/>
			<w:rsid w:val="00450EF6"/>
			<w:pPr>
				<w:pBdr>
					<w:bottom w:val="single" w:sz="6" wx:bdrwidth="15" w:space="1" w:color="auto"/>
				</w:pBdr>
				<w:tabs>
					<w:tab w:val="center" w:pos="4153"/>
					<w:tab w:val="right" w:pos="8306"/>
				</w:tabs>
				<w:snapToGrid w:val="off"/>
				<w:jc w:val="center"/>
			</w:pPr>
			<w:rPr>
				<wx:font wx:val="Calibri"/>
				<w:sz w:val="18"/>
				<w:sz-cs w:val="18"/>
			</w:rPr>
		</w:style>
		<w:style w:type="character" w:styleId="Char">
			<w:name w:val="页眉 Char"/>
			<w:basedOn w:val="a0"/>
			<w:link w:val="a3"/>
			<w:rsid w:val="00450EF6"/>
			<w:rPr>
				<w:sz w:val="18"/>
				<w:sz-cs w:val="18"/>
			</w:rPr>
		</w:style>
		<w:style w:type="paragraph" w:styleId="a4">
			<w:name w:val="footer"/>
			<wx:uiName wx:val="页脚"/>
			<w:basedOn w:val="a"/>
			<w:link w:val="Char0"/>
			<w:rsid w:val="00450EF6"/>
			<w:pPr>
				<w:tabs>
					<w:tab w:val="center" w:pos="4153"/>
					<w:tab w:val="right" w:pos="8306"/>
				</w:tabs>
				<w:snapToGrid w:val="off"/>
				<w:jc w:val="left"/>
			</w:pPr>
			<w:rPr>
				<wx:font wx:val="Calibri"/>
				<w:sz w:val="18"/>
				<w:sz-cs w:val="18"/>
			</w:rPr>
		</w:style>
		<w:style w:type="character" w:styleId="Char0">
			<w:name w:val="页脚 Char"/>
			<w:basedOn w:val="a0"/>
			<w:link w:val="a4"/>
			<w:rsid w:val="00450EF6"/>
			<w:rPr>
				<w:sz w:val="18"/>
				<w:sz-cs w:val="18"/>
			</w:rPr>
		</w:style>
		<w:style w:type="table" w:styleId="a5">
			<w:name w:val="Table Grid"/>
			<wx:uiName wx:val="网格型"/>
			<w:basedOn w:val="a1"/>
			<w:rsid w:val="00450EF6"/>
			<w:rPr>
				<wx:font wx:val="Calibri"/>
			</w:rPr>
			<w:tblPr>
				<w:tblInd w:w="0" w:type="dxa"/>
				<w:tblBorders>
					<w:top w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="000000"/>
					<w:left w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="000000"/>
					<w:bottom w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="000000"/>
					<w:right w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="000000"/>
					<w:insideH w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="000000"/>
					<w:insideV w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="000000"/>
				</w:tblBorders>
				<w:tblCellMar>
					<w:top w:w="0" w:type="dxa"/>
					<w:left w:w="108" w:type="dxa"/>
					<w:bottom w:w="0" w:type="dxa"/>
					<w:right w:w="108" w:type="dxa"/>
				</w:tblCellMar>
			</w:tblPr>
		</w:style>
	</w:styles>
	<w:shapeDefaults>
		<o:shapedefaults v:ext="edit" spidmax="3074"/>
		<o:shapelayout v:ext="edit">
			<o:idmap v:ext="edit" data="2"/>
		</o:shapelayout>
	</w:shapeDefaults>
	<w:docPr>
		<w:view w:val="print"/>
		<w:zoom w:percent="100"/>
		<w:doNotEmbedSystemFonts/>
		<w:bordersDontSurroundHeader/>
		<w:bordersDontSurroundFooter/>
		<w:defaultTabStop w:val="420"/>
		<w:drawingGridVerticalSpacing w:val="156"/>
		<w:displayHorizontalDrawingGridEvery w:val="0"/>
		<w:displayVerticalDrawingGridEvery w:val="2"/>
		<w:punctuationKerning/>
		<w:characterSpacingControl w:val="CompressPunctuation"/>
		<w:optimizeForBrowser/>
		<w:validateAgainstSchema/>
		<w:saveInvalidXML w:val="off"/>
		<w:ignoreMixedContent w:val="off"/>
		<w:alwaysShowPlaceholderText w:val="off"/>
		<w:hdrShapeDefaults>
			<o:shapedefaults v:ext="edit" spidmax="3074"/>
		</w:hdrShapeDefaults>
		<w:footnotePr>
			<w:footnote w:type="separator">
				<w:p wsp:rsidR="00901BEB" wsp:rsidRDefault="00901BEB" wsp:rsidP="00450EF6">
					<w:r>
						<w:separator/>
					</w:r>
				</w:p>
			</w:footnote>
			<w:footnote w:type="continuation-separator">
				<w:p wsp:rsidR="00901BEB" wsp:rsidRDefault="00901BEB" wsp:rsidP="00450EF6">
					<w:r>
						<w:continuationSeparator/>
					</w:r>
				</w:p>
			</w:footnote>
		</w:footnotePr>
		<w:endnotePr>
			<w:endnote w:type="separator">
				<w:p wsp:rsidR="00901BEB" wsp:rsidRDefault="00901BEB" wsp:rsidP="00450EF6">
					<w:r>
						<w:separator/>
					</w:r>
				</w:p>
			</w:endnote>
			<w:endnote w:type="continuation-separator">
				<w:p wsp:rsidR="00901BEB" wsp:rsidRDefault="00901BEB" wsp:rsidP="00450EF6">
					<w:r>
						<w:continuationSeparator/>
					</w:r>
				</w:p>
			</w:endnote>
		</w:endnotePr>
		<w:compat>
			<w:spaceForUL/>
			<w:balanceSingleByteDoubleByteWidth/>
			<w:doNotLeaveBackslashAlone/>
			<w:ulTrailSpace/>
			<w:doNotExpandShiftReturn/>
			<w:adjustLineHeightInTable/>
			<w:breakWrappedTables/>
			<w:snapToGridInCell/>
			<w:wrapTextWithPunct/>
			<w:useAsianBreakRules/>
			<w:dontGrowAutofit/>
			<w:useFELayout/>
		</w:compat>
		<wsp:rsids>
			<wsp:rsidRoot wsp:val="00450EF6"/>
			<wsp:rsid wsp:val="001150A2"/>
			<wsp:rsid wsp:val="003A1433"/>
			<wsp:rsid wsp:val="003C1476"/>
			<wsp:rsid wsp:val="003D6863"/>
			<wsp:rsid wsp:val="00450EF6"/>
			<wsp:rsid wsp:val="00550C2D"/>
			<wsp:rsid wsp:val="00564E9C"/>
			<wsp:rsid wsp:val="00571341"/>
			<wsp:rsid wsp:val="005F0FB5"/>
			<wsp:rsid wsp:val="006E6370"/>
			<wsp:rsid wsp:val="00736DC6"/>
			<wsp:rsid wsp:val="007B3757"/>
			<wsp:rsid wsp:val="00901BEB"/>
			<wsp:rsid wsp:val="00951B35"/>
			<wsp:rsid wsp:val="009B3291"/>
			<wsp:rsid wsp:val="009E6076"/>
			<wsp:rsid wsp:val="00AF02BE"/>
			<wsp:rsid wsp:val="00C645D8"/>
			<wsp:rsid wsp:val="00D9542F"/>
			<wsp:rsid wsp:val="00DD2FF0"/>
			<wsp:rsid wsp:val="00EF1A75"/>
			<wsp:rsid wsp:val="00F102FA"/>
		</wsp:rsids>
	</w:docPr>
	<w:body>
		<w:tbl>
			<w:tblPr>
				<w:tblW w:w="0" w:type="auto"/>
				<w:tblBorders>
					<w:top w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="000000"/>
					<w:left w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="000000"/>
					<w:bottom w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="000000"/>
					<w:right w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="000000"/>
					<w:insideH w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="000000"/>
					<w:insideV w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="000000"/>
				</w:tblBorders>
				<w:tblLook w:val="04A0"/>
			</w:tblPr>
			<w:tblGrid>
				<w:gridCol w:w="8522"/>
			</w:tblGrid>
			<w:tr wsp:rsidR="00EF1A75" wsp:rsidRPr="00DD2FF0" wsp:rsidTr="00DD2FF0">
				<w:tc>
					<w:tcPr>
						<w:tcW w:w="8522" w:type="dxa"/>
					</w:tcPr>
					<w:p wsp:rsidR="00EF1A75" wsp:rsidRPr="00DD2FF0" wsp:rsidRDefault="00EF1A75" wsp:rsidP="00DD2FF0">
						<w:pPr>
							<w:jc w:val="center"/>
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
								<w:b/>
								<w:sz w:val="44"/>
								<w:sz-cs w:val="44"/>
							</w:rPr>
						</w:pPr>
						<w:r wsp:rsidRPr="00DD2FF0">
							<w:rPr>
								<wx:font wx:val="宋体"/>
								<w:b/>
								<w:sz w:val="44"/>
								<w:sz-cs w:val="44"/>
							</w:rPr>
							<w:t>案件延期</w:t>
						</w:r>
						<w:r wsp:rsidRPr="00DD2FF0">
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
								<wx:font wx:val="宋体"/>
								<w:b/>
								<w:sz w:val="44"/>
								<w:sz-cs w:val="44"/>
							</w:rPr>
							<w:t>申请记录</w:t>
						</w:r>
					</w:p>
					<w:p wsp:rsidR="00EF1A75" wsp:rsidRPr="00736DC6" wsp:rsidRDefault="00EF1A75" wsp:rsidP="00DD2FF0">
						<w:pPr>
							<w:jc w:val="center"/>
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
							</w:rPr>
						</w:pPr>
					</w:p>
					<w:p wsp:rsidR="00EF1A75" wsp:rsidRPr="00DD2FF0" wsp:rsidRDefault="00EF1A75" wsp:rsidP="00EF1A75">
						<w:pPr>
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
								<w:sz w:val="28"/>
								<w:sz-cs w:val="28"/>
							</w:rPr>
						</w:pPr>
						<w:r wsp:rsidRPr="00DD2FF0">
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
								<wx:font wx:val="宋体"/>
								<w:sz w:val="28"/>
								<w:sz-cs w:val="28"/>
							</w:rPr>
							<w:t>案件名称:</w:t>
						</w:r>
						<w:r>
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
							</w:rPr>
							<w:t>${mc}</w:t>
						</w:r>
					</w:p>
					<w:p wsp:rsidR="00EF1A75" wsp:rsidRPr="00DD2FF0" wsp:rsidRDefault="00EF1A75" wsp:rsidP="00EF1A75">
						<w:pPr>
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
								<w:sz w:val="28"/>
								<w:sz-cs w:val="28"/>
							</w:rPr>
						</w:pPr>
						<w:r wsp:rsidRPr="00DD2FF0">
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
								<wx:font wx:val="宋体"/>
								<w:sz w:val="28"/>
								<w:sz-cs w:val="28"/>
							</w:rPr>
							<w:t>申请理由:</w:t>
						</w:r>
						<w:r>
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
							</w:rPr>
							<w:t>${ly}</w:t>
						</w:r>
					</w:p>
					<w:p wsp:rsidR="00EF1A75" wsp:rsidRPr="00DD2FF0" wsp:rsidRDefault="00EF1A75" wsp:rsidP="00EF1A75">
						<w:pPr>
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
								<w:sz w:val="28"/>
								<w:sz-cs w:val="28"/>
							</w:rPr>
						</w:pPr>
						<w:r wsp:rsidRPr="00DD2FF0">
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
								<wx:font wx:val="宋体"/>
								<w:sz w:val="28"/>
								<w:sz-cs w:val="28"/>
							</w:rPr>
							<w:t>延期日期:</w:t>
						</w:r>
						<w:r>
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
							</w:rPr>
							<w:t>${rq}</w:t>
						</w:r>
					</w:p>
					<w:p wsp:rsidR="00EF1A75" wsp:rsidRPr="00DD2FF0" wsp:rsidRDefault="00EF1A75" wsp:rsidP="00EF1A75">
						<w:pPr>
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
								<w:sz w:val="28"/>
								<w:sz-cs w:val="28"/>
							</w:rPr>
						</w:pPr>
						<w:r wsp:rsidRPr="00DD2FF0">
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
								<wx:font wx:val="宋体"/>
								<w:sz w:val="28"/>
								<w:sz-cs w:val="28"/>
							</w:rPr>
							<w:t>申请人:</w:t>
						</w:r>
						<w:r>
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
							</w:rPr>
							<w:t>${sqr}</w:t>
						</w:r>
					</w:p>
					<w:p wsp:rsidR="00EF1A75" wsp:rsidRPr="00DD2FF0" wsp:rsidRDefault="00EF1A75" wsp:rsidP="00EF1A75">
						<w:pPr>
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
								<w:sz w:val="28"/>
								<w:sz-cs w:val="28"/>
							</w:rPr>
						</w:pPr>
						<w:r wsp:rsidRPr="00DD2FF0">
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
								<wx:font wx:val="宋体"/>
								<w:sz w:val="28"/>
								<w:sz-cs w:val="28"/>
							</w:rPr>
							<w:t>是否同意:</w:t>
						</w:r>
						<w:r>
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
							</w:rPr>
							<w:t>${sfty}</w:t>
						</w:r>
					</w:p>
					<w:p wsp:rsidR="00EF1A75" wsp:rsidRPr="00EF1A75" wsp:rsidRDefault="00EF1A75" wsp:rsidP="00EF1A75">
						<w:pPr>
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
							</w:rPr>
						</w:pPr>
						<w:r wsp:rsidRPr="00DD2FF0">
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
								<wx:font wx:val="宋体"/>
								<w:sz w:val="28"/>
								<w:sz-cs w:val="28"/>
							</w:rPr>
							<w:t>领导意见:</w:t>
						</w:r>
						<w:r>
							<w:rPr>
								<w:rFonts w:hint="fareast"/>
							</w:rPr>
							<w:t>${yj}</w:t>
						</w:r>
					</w:p>
				</w:tc>
			</w:tr>
		</w:tbl>
		<w:p wsp:rsidR="00EF1A75" wsp:rsidRDefault="00EF1A75" wsp:rsidP="00EF1A75">
			<w:pPr>
				<w:rPr>
					<w:rFonts w:hint="fareast"/>
					<w:b/>
					<w:sz w:val="44"/>
					<w:sz-cs w:val="44"/>
				</w:rPr>
			</w:pPr>
		</w:p>
		<w:sectPr wsp:rsidR="00EF1A75">
			<w:pgSz w:w="11906" w:h="16838"/>
			<w:pgMar w:top="1440" w:right="1800" w:bottom="1440" w:left="1800" w:header="851" w:footer="992" w:gutter="0"/>
			<w:cols w:space="425"/>
			<w:docGrid w:type="lines" w:line-pitch="312"/>
		</w:sectPr>
	</w:body>
</w:wordDocument>

之后拟写JAVA程序对标签的字符直接替换,并保存为DOC即可~需要引入freemarker.jar包,此方法是用JAVA生成复杂word最可行的方法之一。

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
package com.ll19.test;
 
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
 
import freemarker.template.Configuration;
import freemarker.template.Template;
 
public class CreateDoc2 {
 
	/**
	 * 
	 * 
	 * 1.将处理好的的word模板,另存为xml格式的文档。并在其中添加参数: ${export}
	 * 
	 * 2.将xml文档修改完成后,保存并修改后缀名为“****.ftl”; 3.编写java类,主要实现对模板的赋值以及生成流,用于导出。
	 * 
	 */
 
	public static void main(String[] args) throws FileNotFoundException, IOException {
 
		HashMap<String, String> map = new HashMap<String, String>();
 
		map.put("mc", "你大爷");
		map.put("ly", "理由");
		map.put("rq", "日期!!");
		map.put("sqr", "申请人!!");
		map.put("sfty", "是否同意");
		map.put("yj", "领导意见!");
 
		Configuration configuration = new Configuration();
		configuration.setDefaultEncoding("utf-8");
		configuration.setClassForTemplateLoading(CreateDoc2.class, "/com/ll19/test");
		Template t = null;
		t = configuration.getTemplate("test.ftl");
		File outFile = new File("D:/outFile.doc");
		FileOutputStream fos = new FileOutputStream(outFile);
		OutputStreamWriter oWriter = new OutputStreamWriter(fos, "utf-8");// 这个地方对流的编码不可或缺,
		Writer out = null;
		try {
			out = new BufferedWriter(oWriter);
			t.process(map, out);
		} catch (Exception e) {
			e.printStackTrace();
		}
		out.close();
 
	}
}

关于用户登陆中使用RSA非对称加密

包括QQ在内的很多站点在没有使用https的情况下,基本都是RSA对用户的密码进行加密后传输的,现在很多论坛的AJAX登陆,比如百度贴吧,都不是在HTTPS中进行的,为了安全起见,防止用户密码的明文传输,用RSA对其进行加密是个不错的选择。最近也接触了一下这方面的内容,找了一些资料,完成了前端JS加密后台JAVA解密的完整实例,记录一下。

首先下载http://ohdave.com/rsa/提供的RSA in JavaScript,包括三个文件:Multiple-precision library,Barrett modular reduction library,RSA library。通过JS进行RSA加解密,并且可以将加密的内容转换成16进制字符,以供提交。

以下为具体的前端代码:

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
<%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html:html lang="true">
  <head>
  <title>login</title>
  <script type="text/javascript" src="resources/js/RSA.js"></script>
  <script type="text/javascript" src="resources/js/BigInt.js"></script>
  <script type="text/javascript" src="resources/js/Barrett.js"></script>
  <script type="text/javascript">  
		function rsalogin() {
			var thisPwd = document.getElementById("password").value;
			if (thisPwd == "") {
				alert("no value");
				return;
			}
			bodyRSA();
			var result = encryptedString(key, encodeURIComponent(thisPwd));
			// alert(encodeURIComponent(thisPwd)+"\r\n"+result);
			document.getElementById("password").value = result;
			loginForm.action = "loginCHK.jsp";
			loginForm.submit();
		}
		var key;
		function bodyRSA() {
			setMaxDigits(131);
			key = new RSAKeyPair(
					"10001",
					"",
					"9bef88ab36a99e2d234a207a7db70f686bfa680d3e4da6cb0c3eb92adaef1264f0b68791fb0ff985eef4a0454e3280578be8970454213dbe842f523870956a14eee955675af23667428b046735dc2d6c6d814db78a1e08e832959b0c8a1113c079766d56e19ceae7c3618be3c60a4ed5cef03903f4b2ee8ede5649b95f1a64b7");
		}
    </script>
  </head>
  <body>
  <form method="post" name="loginForm" target="_blank">
    <p>Password:
      <input type='text' name="password" id="password" style='width:400px' value="passwd"/>
    </p>
    <p>
      <input type="button" value="submit" onClick="rsalogin();" />
    </p>
  </form>
  </body>
</html:html>

需要设置好生成的公匙,调用加密方法,生成16进制的加密字符即可。关于此处的字符数量限制,作者写的BigInt我没有细看,基本应该是用作将字节组转换为16进制字符,同JAVA的BigInteger,此处的加密字符长度应该不能太大,否则转换过程中会出现问题。总长度不要超过126(1汉字长度为9 汉字不要超过14个字),对于密码的加解密已经完全够用。

提交的loginCHK.jsp~

1
2
3
4
5
6
7
8
<%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
<jsp:directive.page import="com.ll19.rsa.LoginAction"/>
<%
LoginAction la = new LoginAction();
la.execute(request ,response);
%>
 
pwd is [<%=request.getAttribute("pwd")%>]

之后便是后台解密,需要用到bcprov-jdk16-146.jar这个包。

bcprov-jdk16-146.jar是基于jdk的加密算法的具体实现,算法包括MD5,SHA-1,DES,DESede,RSA等。下载地址http://www.bouncycastle.org~

后台代码:

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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package com.ll19.rsa;
 
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
 
import javax.crypto.Cipher;
 
/**
 * RSA 工具类。提供加密,解密,生成密钥对等方法。
 * 需要bcprov(基于jdk的加密算法的具体实现,算法包括MD5,SHA-1,DES,DESede,RSA等-)
 * 
 * 下载地址http://www.bouncycastle.org
 * 
 * 因为后台要转换成bigint,所以对明文长度有限制 总长度不超过126(1汉字长度为9 汉字不要超过14个字)
 * 
 */
public class RSAUtil {
 
	/**
	 * 加密
	 */
	public static byte[] encrypt(PublicKey pk, byte[] data) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(Cipher.ENCRYPT_MODE, pk);
			int blockSize = cipher.getBlockSize();// 获得加密块大小,如:加密前数据为128个byte,而key_size=1024
			// 加密块大小为127
			// byte,加密后为128个byte;因此共有2个加密块,第一个127
			// byte第二个为1个byte
			int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
			int leavedSize = data.length % blockSize;
			int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize;
			byte[] raw = new byte[outputSize * blocksSize];
			int i = 0;
			while (data.length - i * blockSize > 0) {
				if (data.length - i * blockSize > blockSize)
					cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize);
				else
					cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize);
				// 这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到
				// ByteArrayOutputStream中,而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了
				// OutputSize所以只好用dofinal方法。
 
				i++;
			}
			return raw;
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}
 
	/**
	 * 解密
	 */
	public static byte[] decrypt(PrivateKey pk, byte[] raw) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(cipher.DECRYPT_MODE, pk);
			int blockSize = cipher.getBlockSize();
			ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
			int j = 0;
 
			while (raw.length - j * blockSize > 0) {
				bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
				j++;
			}
			return bout.toByteArray();
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}
 
	/**
	 * 
	 * 根据本地的RSAKey文件获取KeyPair
	 * 
	 * @throws Exception
	 */
	public static KeyPair getKeyPair(String rsaKeyStore) throws Exception {
		FileInputStream fis = new FileInputStream(rsaKeyStore);
		ObjectInputStream oos = new ObjectInputStream(fis);
		KeyPair kp = (KeyPair) oos.readObject();
		oos.close();
		fis.close();
		return kp;
	}
 
	/**
	 * 
	 * 存储KeyPair到本地
	 * 
	 * @throws Exception
	 */
	public static void saveKeyPair(KeyPair kp, String path) throws Exception {
		FileOutputStream fos = new FileOutputStream(path);
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		// 生成密钥
		oos.writeObject(kp);
		oos.close();
		fos.close();
	}
 
	/**
	 * 
	 * 用于生成公匙或私匙
	 * 
	 * @throws NoSuchAlgorithmException
	 * 
	 */
	public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
 
		SecureRandom sr = new SecureRandom();
		KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
		// 注意密钥大小最好为1024,否则解密会有乱码情况.
		kg.initialize(1024, sr);
		KeyPair genKeyPair = kg.genKeyPair();
		return genKeyPair;
 
	}
 
	/**
	 * 
	 * 测试
	 * 
	 */
	public static void main(String[] args) throws Exception {
 
		// 获取公匙及私匙
		KeyPair generateKeyPair = generateKeyPair();
 
		// 公匙 用于前台加密
		PublicKey publicKey = generateKeyPair.getPublic();
		System.out.println(publicKey);
 
		// 私匙 存储在后台用于解密
		PrivateKey privateKey = generateKeyPair.getPrivate();
		System.out.println(privateKey);
 
		// 存储KeyPair到本地用于后期解密 注意修改前台RSAKeyPair
		// saveKeyPair(generateKeyPair,"D:\\LL19.com\\webRoot\\RSA\\build\\webapp\\WEB-INF\\class\\META-INF\\rsa\\RSAKey");
 
		// 测试加密解密
		String test = "123331344412124";
		// test = "阿斯顿发送对发生地发送盗伐水电费圣达菲sadfsadf爱上对方爱上对方";
		test = "aaaaaaa";
 
		byte[] en_test = encrypt(publicKey, test.getBytes());
		System.out.println("加密后字符:" + new String(en_test));
 
		byte[] de_test = decrypt(privateKey, en_test);
		System.out.println("解密后字符:" + new String(de_test));
 
	}
 
}
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package com.ll19.rsa;
 
import java.io.ByteArrayOutputStream;
 
public class BytesUtil {
 
	private static String hexString = "0123456789ABCDEF";
 
	public static String stringToHexString(String strPart) {
 
		String hexString = "";
 
		for (int i = 0; i < strPart.length(); i++) {
			int ch = (int) strPart.charAt(i);
			String strHex = Integer.toHexString(ch);
			hexString = hexString + strHex;
		}
 
		return hexString;
 
	}
 
	/*
	 * 
	 * 将字符串编码成16进制数字,适用于所有字符(包括中文)
	 */
	public static String encode(String str) {
 
		// 根据默认编码获取字节数组
 
		byte[] bytes = str.getBytes();
		StringBuilder sb = new StringBuilder(bytes.length * 2);
 
		// 将字节数组中每个字节拆解成2位16进制整数
		for (int i = 0; i < bytes.length; i++) {
			sb.append(hexString.charAt((bytes[i] & 0xf0) >> 4));
			sb.append(hexString.charAt((bytes[i] & 0x0f) >> 0));
		}
 
		return sb.toString();
 
	}
 
	/*
	 * 
	 * 将16进制数字解码成字符串,适用于所有字符(包括中文)
	 */
	public static String decode(String bytes) {
 
		ByteArrayOutputStream baos = new ByteArrayOutputStream(bytes.length() / 2);
 
		// 将每2位16进制整数组装成一个字节
		for (int i = 0; i < bytes.length(); i += 2)
			baos.write((hexString.indexOf(bytes.charAt(i)) << 4 | hexString.indexOf(bytes.charAt(i + 1))));
		return new String(baos.toByteArray());
 
	}
 
	private static byte uniteBytes(byte src0, byte src1) {
 
		byte _b0 = Byte.decode("0x" + new String(new byte[] { src0 })).byteValue();
		_b0 = (byte) (_b0 << 4);
		byte _b1 = Byte.decode("0x" + new String(new byte[] { src1 })).byteValue();
		byte ret = (byte) (_b0 | _b1);
 
		return ret;
 
	}
 
	public static byte[] hexString2Bytes(String src) {
 
		byte[] ret = new byte[6];
		byte[] tmp = src.getBytes();
		for (int i = 0; i < 6; ++i) {
			ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]);
		}
 
		return ret;
 
	}
 
	/**
	 * 
	 * Convert byte[] to hex
	 * string.这里我们可以将byte转换成int,然后利用Integer.toHexString(int)来转换成16进制字符串。
	 * 
	 * @param src
	 *            byte[] data
	 * 
	 * @return hex string
	 */
	public static String bytesToHexString(byte[] src) {
 
		StringBuilder stringBuilder = new StringBuilder("");
 
		if (src == null || src.length <= 0) {
			return null;
		}
 
		for (int i = 0; i < src.length; i++) {
			int v = src[i] & 0xFF;
			String hv = Integer.toHexString(v);
			if (hv.length() < 2) {
				stringBuilder.append(0);
			}
			stringBuilder.append(hv);
		}
 
		return stringBuilder.toString();
 
	}
 
	/**
	 * 
	 * Convert hex string to byte[]
	 * 
	 * @param hexString
	 *            the hex string
	 * 
	 * @return byte[]
	 */
	public static byte[] hexStringToBytes(String hexString) {
 
		if (hexString == null || hexString.equals("")) {
			return null;
		}
 
		hexString = hexString.toUpperCase();
		int length = hexString.length() / 2;
		char[] hexChars = hexString.toCharArray();
		byte[] d = new byte[length];
		for (int i = 0; i < length; i++) {
			int pos = i * 2;
			d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
		}
 
		return d;
 
	}
 
	/**
	 * 
	 * Convert char to byte
	 * 
	 * @param c
	 *            char
	 * 
	 * @return byte
	 */
	private static byte charToByte(char c) {
		return (byte) "0123456789ABCDEF".indexOf(c);
	}
 
}
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
package com.ll19.rsa;
 
import java.net.URLDecoder;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class LoginAction {
 
	public boolean execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		String pwd;
 
		String result = request.getParameter("password");
		System.out.println("原文加密后为:");
		System.out.println(result);
 
		// 如果16进制直接转字节数组 有时会发生错误 第一位是0 导致无法正常解密
		// byte[] en_result = new BigInteger(result, 16).toByteArray();
 
		// 下面的方法解决上述问题 经测试 已经可以完美加解密任何字符
		byte[] en_result = BytesUtil.hexStringToBytes(result);
		// System.out.println("转成byte[]" + new String(en_result));
 
		String rsaKeyStore = request.getSession().getServletContext().getRealPath("/") + "WEB-INF/class/META-INF/rsa/RSAKey";
 
		byte[] de_result = RSAUtil.decrypt(RSAUtil.getKeyPair(rsaKeyStore).getPrivate(), en_result);
		System.out.println("还原密文:");
		System.out.println(new String(de_result));
		StringBuffer sb = new StringBuffer();
		sb.append(new String(de_result));
		pwd = sb.reverse().toString();
		System.out.println(sb);
		System.out.println("=================================");
		pwd = URLDecoder.decode(pwd, "UTF-8");//  
		System.out.println(pwd);
		request.setAttribute("pwd", pwd);
		return true;
	}
}

说明一下,多数人找到的可能都是这个例子,原作者提供的内容有处错误,前台加密后的16进制字符串直接通过BigInteger转换成字节组 byte[] en_result = new BigInteger(result, 16).toByteArray(); 直接这样转换会导致很多字符无法解密,此处我找了一个16进制转字节组的方法替代,可以完全加解密所有字符。

RSAUtil中的generateKeyPair()用于生成公匙和对应的私匙。

对于加密后转换为字符传输,用Base64其实也是不错的选择,Base64编码后,长度会比原字节数组长三分之二;十六进制编码,则会长一倍。

360° FLASH MP3播放器-19~waveCircle

提供下载:360° FLASH MP3播放器-19~waveCircle,此播放器包括了歌词同步和很炫的波谱效果~

这是我参考了schillmania.com提供的源码,他所提供的是AS+JS版,我也不明白为什么他不直接写成FLASH,大家有兴趣可以点击链接去看看原AS+JS版的效果。我参考了部分写法(其实我只参考了他的设计思路,真正挪用的源码只有SoundProcessor这一个类,其余的部分全是按照自己想法在写),并且加入了相关的列表播放、歌词同步、波谱缓冲及个性化配置等功能,改了一个纯FLASH的播放器。

整个播放器自适应大小,你可以随意调整高宽,通过不同配置(例如歌曲地址、歌词lrc地址、字体颜色大小、布局、波形颜色等),生成不同大小及颜色的播放器。以下为此播放器的所有配置:

播放器的相关配置:

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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//是否自动播放 yes(自动) no random(随机)
public static var autoPlay : String = "no";
 
//上边FPS空出的距离
public static var topFpsSpace : Number = 30;
//下边MP3歌曲名空出的距离
public static var bottomTitleSpace : Number = 25;
//下边歌词空出的距离
public static var bottomLrcSpace : Number = 25;
 
//显示波谱的距离
public static var waveSpace : Number = 20;
 
//圆环的宽
public static var radius : Number = 35;
 
//影片质量
public static var quality : String = "medium";
 
//主背景颜色
public static var backgroundColor : uint = 0xFFFFFF;
 
//FPS的颜色和字体
public static var fpsTextColor : uint = 0x333333;
public static var fpsTextFace : String = "Georgia";
//是否显示FPS
public static var fps : Boolean = false;
//FPS的上部填充
public static var fpsMargin : Number = 15;
 
//BAR的高度
public static var barH : Number = 23;
//BAR的背景
public static var barBg : Number = 0xF7F7F7;
//BAR的边框
public static var barBorderColor : Number = 0xDDDDDD;
 
//按钮移入左上边框颜色
public static var barButtonLTOverColor : uint = 0x999999;
//按钮移入右下边框颜色
public static var barButtonRBOverColor : uint = 0x333333;
 
//按钮激活状态的背景1
public static var barButtonOnBg1 : uint = 0xFFFFFF;
//按钮激活状态的背景2
public static var barButtonOnBg2 : uint = 0xDDDDDD;
 
//提示工具的透明度
public static var toolTipsAlpha : Number = 0.9;
//提示工具的边框色
public static var toolTipsBorderColor : uint = 0x222222;
//提示工具的背景色
public static var toolTipsBackgroundColor : uint = 0xffffff;
//提示工具的字体色
public static var toolTipsTextColor : uint = 0x333333;
//提示工具的X偏移
public static var toolTipsEx : Number = 15;
//提示工具的Y偏移
public static var toolTipsEy : Number = 20;
//提示工具的字体大小
public static var toolTipsTextFace : String = "Georgia";
public static var toolTipsTextSize : Number = 12;
 
//BarList按钮左侧方框边框颜色
public static var barListLeftBorderColor : uint = 0x777777;
//BarList按钮右侧线颜色
public static var barListRightLineColor : uint = 0x777777;
 
//列表的字体颜色大小
public static var listTextColor : uint = 0x777777;
public static var listTextFace : String = "Georgia";
public static var listTextSize : Number = 9;
//各个列表的间距
public static var listTextHSpace : Number = 6;
 
//默认的播放的音量(0到1)
public static var volume : Number = 0.2;
//音量按钮边框颜色
public static var volumeButtonBorderColor : uint = 0x777777;
//音量按钮颜色
public static var volumeButtonColor : uint = 0xFFFFFF;
 
//音量的高
public static var volumeBarH : Number = 60;
//音量的宽
public static var volumeBarW : Number = 8;
//音量的颜色
public static var volumeColor : uint = 0x555555;
 
//左右音量幅度按钮的背景颜色 由下到上
public static var barPeakBg1 : uint = 0xAAAAAA;
public static var barPeakBg2 : uint = 0x999999;
public static var barPeakBg3 : uint = 0x888888;
public static var barPeakBg4 : uint = 0x777777;
//左右音量幅度按钮的颜色 由下到上
public static var barPeak1 : uint = 0x00CC00;
public static var barPeak2 : uint = 0x33FF00;
public static var barPeak3 : uint = 0xFF9900;
public static var barPeak4 : uint = 0xFF0000;
//音量幅度的缓冲
public static var updateBarPeakNumber : Number = 0.8;
 
//MP3歌曲名的颜色和字体、大小
public static var titleTextColor : uint = 0x333333;
public static var titleTextFace : String = "Georgia";
public static var titleTextSize : Number = 10;
 
//lrc的颜色和字体、大小
public static var lrcTextColor : uint = 0x333333;
public static var lrcTextFace : String = "Georgia";
public static var lrcTextSize : Number = 10;
//每句和每句歌词间的间距
public static var lrcSpace : Number = 10;
 
//十字坐标的颜色
public static var crossColor : uint = 0xF2F2F2;
 
//圆环的背景色
public static var circleBgColor : uint = 0xEEEEEE;
//圆环背景的边线颜色
public static var circleBgBorderColor : uint = 0xEEEEEE;
 
//圆环加载色
public static var circleLoaderColor : uint = 0xCCCCCC;
//圆环加载的边线颜色
public static var circleLoaderBorderColor : uint = 0xCCCCCC;
 
//圆环播放色
public static var circlePlayColor : uint = 0x222222;
//圆环播放的边线颜色
public static var circlePlayBorderColor : uint = 0x222222;
//播放进度文字的颜色
public static var playTextColor : uint = 0x333333;
//播放进度文字的字体
public static var playTextFace : String = "Verdana";
//播放进度文字的大小
public static var playTextSize : Number = 18;
 
//停止及播放按钮的颜色
public static var playButtonColor : uint = 0x333333;
//停止及播放按钮的长宽(正方形)
public static var buttonWidth : Number = 16;
 
//更新波谱的时间(毫秒) 设置的FPS为50 即每20MS播放一帧 下面设置的updateWaveTime除以20得到的数字越大 缓冲的越好 
//不过同样updateWaveTime越大更新波谱的频率越低 
public static var updateWaveTime : Number = 50;
//波谱每次缓冲的值(小于1)
public static var updateWaveNumber : Number = 0.6;
//波谱每次比例缓冲的值(小于1)
public static var updateWaveScaleNumber : Number = 0.6;
 
//内部波谱相关
//波谱颜色
public static var waveInColor : uint = 0x0099ff;
//波谱边框颜色
public static var waveInBorderColor : uint = 0x0099ff;
//每个波形对应的角度(相当于宽 可以设置小数)
public static var waveInAng : Number = 1; 
//public static var waveInAng : Number = 0.5; 
//波形和波形之间的间距角度(不要设置为小数 最小为1)
public static var waveInSpaceAng : Number = 2; 
//波形长度的比率(相当于长) 可以改变此值来调整长度
public static var waveInScale : Number = 300; 
//内部波形是否反转
public static var waveInReversal : Boolean = false; 
 
//外部波谱相关 
//波谱未播放部分的颜色
public static var waveOutColor : uint = 0x339933;
//波谱未播放部分的边框颜色
public static var waveOutBorderColor : uint = 0x339933;
//每个波形对应的角度(相当于宽 可以设置小数)
public static var waveOutAng : Number = 3; 
//波形和波形之间的间距角度(不要设置为小数 最小为1)
public static var waveOutSpaceAng : Number = 2; 
//波形长度的比率(相当于长) 可以改变此值来调整长度
public static var waveOutScale : Number = 150; 
//播放部分的颜色
public static var waveOutColorOld : uint = 0x222222;
//播放部分的边框颜色
public static var waveOutBorderColorOld : uint = 0x222222;
 
//波形的比例限制 scale = (1 + (scale * 0.1));
public static var scaleMax : Number = 1.5;

具体配置方法:

播放器需要config.xml做整个播放的配置,配置文件地址可以通过传入比如config=http://www.ll19.com/up/19_waveCircle/config2.xml,如果不传入config参数则默认从FLASH的同级目录下取config.xml做配置文件(例如上方的例子)。

下面是上方实例具体的配置文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
< ?xml version="1.0" encoding="UTF-8"?>
<root>
	<music>
		<song path="http://www.ll19.com/up/19_waveCircle/Blackbird.mp3" title="The Beatles - Blackbird." lrc="http://www.ll19.com/up/19_waveCircle/Blackbird.lrc"/>
		<song path="http://www.ll19.com/up/19_waveCircle/Heroin.mp3" title="The Velvet Underground - Heroin." lrc="http://www.ll19.com/up/19_waveCircle/Heroin.lrc"/>
		<song path="http://www.ll19.com/up/19_waveCircle/tnt.mp3" title="AC/DC - T.N.T." lrc="http://www.ll19.com/up/19_waveCircle/tnt.lrc"/>
		<song path="http://www.ll19.com/up/19_waveCircle/Bubblegoose.mp3" title="South Park - Bubblegoose." lrc="http://www.ll19.com/up/19_waveCircle/Bubblegoose.lrc"/>
		<song path="http://www.ll19.com/up/19_waveCircle/OutsideTheWall.mp3" title="Pink Floyd - Outside The Wall." lrc="http://www.ll19.com/up/19_waveCircle/OutsideTheWall.lrc"/>
		<song path="http://www.ll19.com/up/19_waveCircle/LastNightGoodNight.mp3" title="初音ミク - Last Night,Good Night." lrc="http://www.ll19.com/up/19_waveCircle/LastNightGoodNight.lrc"/>
		<song path="http://www.ll19.com/up/19_waveCircle/Starman.mp3" title="David Bowie - Starman." lrc="http://www.ll19.com/up/19_waveCircle/Starman.lrc"/>
	</music>
	<theme>
		<autoplay>yes</autoplay>
		<volume>0.3</volume>
		<fps>true</fps>
	</theme>
</root>

music节点下的song是指播放的歌曲列表,path为歌曲地址,title为标题,lrc为歌词地址。

theme节点下即为播放器的个性化配置,可以通过传入与静态变量名相同的节点值来配置播放器的显示或运行效果,比如这里的<autoPlay>random</autoPlay>设置为开始随机播放。播放过程中如果当前曲目加载失败会自动加载下一首,播放完毕后也会循环播放下一首。

下面是一个“模拟”只绘制边线的例子:

测试前请先关闭其它播放器。

注意:由于FLASH的安全限制MP3和FLASH播放器如果不在同一域下波谱是无法显示的,所以播放器必须和MP3在同一域下才能显示出效果。

关于绘制,内部波谱使用SoundMixer.computeSpectrum(byteArray, false, 0); 声音波形绘制。外部用SoundMixer.computeSpectrum(byteArray, true, 0); 频谱绘制。

对于看不到波谱效果,可能是你先打开了类似优酷等的视频播放器,或者其他的音乐FLASH播放器造成了冲突,可以先关闭类似的页面后再刷新此页测试。

下面这个是改变了大小、颜色、歌曲并取消自动播放,播放过的时间内部波形颜色反转的效果~

Continue reading

用dwr的comet(推)来实现即时聊天

用comet(推)技术来实现即时聊天。comet是长连接的一种,dwr框架也是Comet的一种,这意味着dwr也可以很轻松的实现服务器的信息推送。使用dwr时,浏览器上可以很轻松地直接调用服务器上的方法,反过来,服务器也可以直接调用浏览器的js。这直接导致双方之间的“通讯”很方便,推送信息很方便。

下面是个人总结的大体思路,具体细节没有细化,如要用到还需在应用中逐渐完善:

首先是DWR的配置,需要在设置中放开服务器推技术,否则无法建立长连接

1
2
3
4
5
		<init-param>
			<description>使用服务器推技术(反转AJAX)</description>
			<param-name>activeReverseAjaxEnabled</param-name>
			<param-value>true</param-value>
		</init-param>

建立一个聊天室需要用到的dwr服务,注册调用方法:

1
2
3
4
5
6
7
8
9
	<!-- 即时聊天 -->
	<bean id="chatDwr" class="ChatDwr">
		<dwr:remote javascript="chatDwr">
			<dwr:include method="updateUsersList"/>
			<dwr:include method="getUsersList"/>
			<dwr:include method="sendMessage"/>
			<dwr:include method="destroySession"/>
		</dwr:remote>
	</bean>

其中updateUsersList为更新聊天室在线用户,getUsersList为获取聊天室在线用户,sendMessage为发送消息,destroySession为退出聊天室注销身份。

大体思路为用userMap来记录所有的聊天室用户,userMap用当前用户的userid作为key来记录此用户的ScriptSession,在给聊天室用户推送消息时便可以用ScriptSession来调用客户端的JS或者直接更改页面的元素。以下为具体实现类:

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
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
 
import javax.servlet.http.HttpServletRequest;
 
import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.ScriptSession;
import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.proxy.dwr.Util;
 
 
/**
 * 即时聊天DWR
 * 
 * @author 19 <br> {@link www.LL19.com}
 */
public class ChatDwr {
 
	/** 当前在线用户列表 */
	public static Map<String, ScriptSession> userMap = new HashMap<String, ScriptSession>();
 
	/**
	 * 更新在线用户列表
	 * 
	 */
	public String updateUsersList(String userID, HttpServletRequest request) {
 
		WebContext wct = WebContextFactory.get();
		ScriptSession scsession = wct.getScriptSession();
		userMap.put(userID, scsession);
 
		// 更新其他客户端用户列表
 
		for (Iterator iterator = userMap.keySet().iterator(); iterator.hasNext();) {
			String userAcc = (String) iterator.next();
			// 当前客户端不用更新 只更新其他客户端
			if (!userAcc.equals(userID)) {
				ScriptSession ss = userMap.get(userAcc);
				Util util = new Util(ss);
 
				// 构造js脚本
				ScriptBuffer scriptBuffer = new ScriptBuffer();
				scriptBuffer.appendScript("result(");
				scriptBuffer.appendData(this.getUsersList(request));
				scriptBuffer.appendScript(")");
				util.addScript(scriptBuffer); // 向客户端推送消息
			}
 
		}
 
		return "0";
	}
 
	public List<Map<String, String>> getUsersList(HttpServletRequest request) throws UserException {
 
		List<Map<String, String>> list = new ArrayList<Map<String, String>>();
 
		for (Iterator iterator = userMap.keySet().iterator(); iterator.hasNext();) {
			String userAcc = (String) iterator.next();
			//在具体的系统中 根据userid获取username 返回信息
			String name = ....;
			Map<String, String> map = new HashMap<String, String>();
			String result = userAcc + ";" + name;
			map.put("key", result);
			list.add(map);
		}
 
		return list;
 
	}
 
	/**
	 * 发送消息
	 * 
	 */
	public String sendMessage(String sender, String receiver, String message, HttpServletRequest request) {
 
		for (Iterator iterator = userMap.keySet().iterator(); iterator.hasNext();) {
			String userAcc = (String) iterator.next();
 
			if (userAcc.equals(receiver)) {
				ScriptSession ss = userMap.get(userAcc);
				Util util = new Util(ss);
 
				// 构造js脚本
				ScriptBuffer scriptBuffer = new ScriptBuffer();
				scriptBuffer.appendScript("pushMessage(");
				scriptBuffer.appendData(message);
				scriptBuffer.appendScript(")");
				util.addScript(scriptBuffer); // 向客户端推送消息
 
				// util.setValue("message", message);
				// util.setStyle("showMessage", "display", "");
			}
		}
 
		return "0";
 
	}
 
	/**
	 * 页面关闭之后用来销毁map中的对象 释放内存
	 * 
	 */
	public String destroySession(String userid, HttpServletRequest request) {
		userMap.remove(userid);
		return "0";
	}
 
}

具体思路是:进入聊天室后,页面加载完毕即执行init()激活反转,更新在线用户,并获取用户列表(之后更新onlineuser的用户列表div)。更新在线用户的同时也向其它客户端推送消息更新用户:

$(window).unload( function () { destroySession(); } );(注此处用了jquery),为用户在退出聊天室的时候调用chatDwr.destroySession(sender);来在userMap中删除自己的在线信息。

JS全局对象sender,receiver为发送接收人的ID,senderName,receiverName为发送接收人的名称,输入内容点击发送按钮即调用chatDwr.sendMessage发送消息并推送信息调用对方客户端JS显示内容。

聊天室页面如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xHTML1/DTD/xHTML1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="zh-CN">
<head>
<title>Chat room</title>
<script type='text/javascript' src='chatDwr.js'></script>
<script type='text/javascript' src='jquery-1.6.2.min.js'></script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
<div id="onlineuser"></div>
<div id="message"> <div id="content"></div> <p id="end" style="height:0px; overflow:hidden;"></p></div></div>
<div id="sender"> 
<p class="senderMessageP"><textarea name="senderMessage" id="senderMessage" class="senderMessage" ></textarea></p>  
<p class="button"><input type="button" value="发送" onClick="javascript:send();"></p>
</div>
<script type="text/javascript">
var sender = "${sender}";
var receiver = "${receiver}";
var senderName = "${senderName}";
var receiverName = "${receiverName}";
var button = "${button}";
</script>
</body>
</html>
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
/**
 * 页面初始化
 */
function init() {
	dwr.engine.setActiveReverseAjax(true); // 激活反转 重要
	chatDwr.updateUsersList(sender); // 更新在线列表
	chatDwr.getUsersList(result);
	//chatDwr。getoNLINEUSER
}
 
function result(data) {
	document.getElementById('onlineuser').innerHTML = '';
  		jQuery.each(data, function(index, content) {
					var id1 = data[index].key.split(";")[0];
					var name1 = data[index].key.split(";")[1];
					if(senderName!=name1){
					document.getElementById('onlineuser').innerHTML += '<div style="margin:8px;" id="'
					+id1+'" ><input  style="margin-right:5px;" id="state" type="radio" name="state" value="'
					+id1+';'+name1+'" onClick="javascript:change(value);"/>'
					+name1+'</div>'; 
					}else{
					document.getElementById('onlineuser').innerHTML +='<div style="margin:8px;padding-left:18px;" >'+name1+'</div>'; 
					 }
				});
 
 
}
function change(value){
	panel1.setTitle(value.split(";")[1]);
	receiverName = value.split(";")[1];
	receiver = value.split(";")[0];
	}
/**
 * 发送消息
 */
function send() {
	if (sender == receiver) {	
		alert ("请在右侧列表选择发送人!");
		return;
	}
	var message = jQuery("#senderMessage").val();
	message = message + "";
	//message = message.replace(/\r\n/ig,"<br/>")   
	message = message.replace(/\r/ig, "<br/>").replace(/\n/ig, "<br/>");
	message = "<span>" + senderName + "</span>" + " " + gettime() + " <br/>" + "<p calss='s' style='margin:0px; padding:5px; margin-left:10px;'>" + message + "</p>";
	chatDwr.sendMessage(sender,receiver,message); // 发送消息
	jQuery("#content").append(message);
	jQuery("#senderMessage").val("");
	document.getElementById("end").scrollIntoView(); 
}
 
 
/**
 * 页面中更新消息
 */
function pushMessage(message) {
	//message = "<span>" + receiverName + "</span>" + " " + gettime() + " <br/>" + "<p calss='r' style='margin:0px; padding:5px; margin-left:10px;'>" + message + "</p>";
	jQuery("#content").append(message);
	document.getElementById("end").scrollIntoView(); 
}
 
var timeout;
function destroySession() {
	chatDwr.destroySession(sender);
	alert ("已退出聊天室。");
}
 
function gettime() {
	var nD=new Date();
	return(nD.getFullYear()+'-'+(nD.getMonth()+1)+'-'+(nD.getDate())+' '+nD.toLocaleTimeString());
}
 
//关闭页面需要清理在线用户 释放内存
$(window).unload( function () { destroySession(); } );

当然多人聊天也是如此,不过是推送给userMap所有人而已,这是一个大体思路,如果真需要建立完善的聊天室,具体实现细节还是要细化。

Cxf+Spring3.0发布webservice的记录

APACHE CXF下载地址:http://cxf.apache.org/,请自行导入CXF和SPRING相关JAR包。

Apache CXF = Celtix + XFire,Apache CXF 的前身叫 Apache CeltiXfire,现在已经正式更名为 Apache CXF 了,以下简称为 CXF。CXF 继承了 Celtix 和 XFire 两大开源项目的精华,提供了对 JAX-WS 全面的支持,并且提供了多种 Binding 、DataBinding、Transport 以及各种 Format 的支持,并且可以根据实际项目的需要,采用代码优先(Code First)或者 WSDL 优先(WSDL First)来轻松地实现 Web Services 的发布和使用。Apache CXF已经是一个正式的Apache顶级项目。

Apache CXF 是一个开源的 Services 框架,CXF 帮助您利用 Frontend 编程 API 来构建和开发 Services ,像 JAX-WS 。这些 Services 可以支持多种协议,比如:SOAP、XML/HTTP、RESTful HTTP 或者 CORBA ,并且可以在多种传输协议上运行,比如:HTTP、JMS 或者 JBI,CXF 大大简化了 Services 的创建,同时它继承了 XFire 传统,一样可以天然地和 Spring 进行无缝集成。

首先注册服务地址托管

1
2
3
4
5
6
7
8
9
10
11
<!--webservice服务地址-->
	<servlet>
		<servlet-name>CXFServlet</servlet-name>
		<servlet-class>.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>CXFServlet</servlet-name>
		<url-pattern>/webservices/*</url-pattern>
	</servlet-mapping>

建立接口和实现类

需要在接口头部注明一个”WebService”注解,表示这是一个webservice。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import javax.jws.WebService;
 
/**
 * 
 * WebService测试接口
 * @author LL19.com
 * 
 */
 
@WebService
public interface Hello {
	public String helloW(String name);
 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import javax.jws.WebService;
 
/**
 * 测试webservice
 * 
 * 此时注解WebService内还有三个属性:endpointInterface表示webservice接口名,因为一个类可以继承多个接口,
 * 你必须指明哪个是webservice接口 serviceName:表示当前webservice的别名
 * portName:表示当前webservice的端口名 这些属性定义好之后,在wsdl中是能看到的,如果不定义,cxf会配置默认的别名和端口名
 * 
 * @author LL19.com
 * 
 */
 
@WebService(endpointInterface = "Hello")
public class HelloImpl implements Hello {
	public String helloW(String name) {
		return name;
	}
 
}

配置servicebean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="GB2312"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:context="http://www.springframework.org/schema/context" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation="http://www.springframework.org/schema/beans 
                   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
                   http://www.springframework.org/schema/context 
                   http://www.springframework.org/schema/context/spring-context-3.0.xsd 
                   http://cxf.apache.org/jaxws  
                   http://cxf.apache.org/schemas/jaxws.xsd">
	<import resource="classpath:META-INF/cxf/cxf.xml"/>
	<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
	<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
	<!--测试webservice-->
	<bean id="helloServicesBean" class="HelloImpl"/>
	<!-- CXF 配置WebServices的服务名及访问地址 -->
	<jaxws:endpoint id="helloServices" implementor="#helloServicesBean" address="/HelloServices.wsdl"/>
</beans>

之后通过http://localhost/webservices/可以看到注册的service。

建立访问客户端

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
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
 
/**
 * 
 * 测试调用wevservice
 * 
 * @author LL19.com
 * 
 */
public class HelloClient {
 
	public static void main(String[] args) {
 
		try {
			JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
			Client client = dcf.createClient("http://localhost:18080/webservices/HelloServices.wsdl?wsdl");
			Object[] res = client.invoke("helloW", new Object[] { "helloWorld" });
			System.out.println(res[0]);
		} catch (Exception e) {
			e.printStackTrace();
		}
 
	}
 
}

接口中不能有构造方法或者仅仅是大小写不同的方法,例如上面接口不能有hello。否则客户端调用JaxWsDynamicClientFactory.createClient的时候,会报错:NoClassDefFoundError,应该是构建类的时候冲突了。