Preface :
export word In fact, there are still many places to use in daily work , So I want to write an article to record it , Before exporting , Need to know about how browsers handle servlet Background data for . We can learn more about it http Communication download behavior in servlet The implementation of the .
== The exported tool class code comes from the network , If there is infringement, please contact me to delete the article ==
personal use ==ftl== As word Export template engine , There are many template engines to choose from , The individual found that ftl It's used a lot , So choose this one
<!-- more -->
Code cloud address :
The article involves more code , If you want to see the operation, you can check my own code cloud address :
https://gitee.com/lazyTimes/i...
Effect demonstration :
Gave a test page , I wrote some scripts on the spot , As a reference ( It will be posted later Html Code in )
Click on the submit , Export content , export word
The report
After export , open word The content is :
Implementation steps - Make word Templates
First step newly build word, Make a prototype of the results
Will need to export word The content of , Paste it to a new one first word In the document
The second step Transfer format -> xml
Select File “ Save as ”, Set the format to xml Format
The third step Format file
Put the document in idea
Or support formatting software , format , preservation :
Pay attention to the place holder
Step four : Template data replacement placeholder
stay word Where the page will need to import data , Replace placeholders
When you need to pay attention to content processing : ${ filename} It can be cut into parts , We need to cut multiple parts , Change it to the following style
Remember that after all the changes , Open it now xml Format word, Make sure it's not broken
The above steps are completed , There is one word The template is ready
Step five : Make ftl file ,word Template forming
Create a new one in the project ftl file , At the same time, you need to configure , At the same time to do a good job in the operation of the station sign xml Put the content in
Code implementation - Export code
- The configuration of the tool class is as follows :
WordGeneratorUtil.java
:
/**
* Template constant class configuration
*/
public static final class FreemarkerTemplate {
public static final String REPORT = "report";
public static final String REC_RECOMMEND = "recRecommend";
// Add your template file name :
}
In a static block of code , You need to inject the corresponding template configuration
// Note that the corresponding template should be loaded during initialization
allTemplates.put(FreemarkerTemplate.REPORT, configuration.getTemplate(FreemarkerTemplate.REPORT + ".ftl"));
allTemplates.put(FreemarkerTemplate.REC_RECOMMEND,configuration.getTemplate(FreemarkerTemplate.REC_RECOMMEND + ".ftl"));
- After configuration , When exporting, you can find the corresponding file
- Establish a general export method :
/**
* establish doc file
* dataMap data , A place holder for the corresponding template is required , Otherwise it will go wrong
* @param dataMap data
* @param wordName word The name of the report
* @param freemarkerTemplateName Specify which freemarker Templates
* @return
*/
public static File createDoc(String freemarkerTemplateName, String wordName, Map<String, String> dataMap) {
try {
File f = new File(wordName);
Template t = allTemplates.get(freemarkerTemplateName);
// This place can't be used FileWriter Because you need to specify the encoding type, otherwise generated Word The document cannot be opened because of an unrecognized encoding
Writer w = new OutputStreamWriter(new FileOutputStream(f), StandardCharsets.UTF_8);
t.process(dataMap, w);
w.close();
return f;
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(" Generate word Document failed ");
}
}
Tool class complete code :
package com.zxd.interview.export;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.util.CollectionUtils;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* A tool class found on the Internet according to information
* Mainly with freemarker For the core template generation word Document tool class
* Fixed path is configured by default
* You need to get the corresponding template according to the path
* The request parameter needs to set the corresponding template name
* @author
* @className: WordGeneratorUtils
* @description: Document generation tool class
* </p>
* version: V1.0.0
*/
public final class WordGeneratorUtil {
private static Configuration configuration = null;
private static Map<String, Template> allTemplates = null;
private static final String TEMPLATE_URL = "/templates";
/**
* Template constant class configuration
*/
public static final class FreemarkerTemplate {
public static final String Test = "test";
public static final String REPORT = "report";
public static final String REC_RECOMMEND = "recRecommend";
}
static {
configuration = new Configuration(Configuration.VERSION_2_3_28);
configuration.setDefaultEncoding("utf-8");
configuration.setClassForTemplateLoading(WordGeneratorUtil.class, TEMPLATE_URL);
allTemplates = new HashMap(4);
try {
// Note that the corresponding template should be loaded during initialization
allTemplates.put(FreemarkerTemplate.Test, configuration.getTemplate(FreemarkerTemplate.Test + ".ftl"));
allTemplates.put(FreemarkerTemplate.REPORT, configuration.getTemplate(FreemarkerTemplate.REPORT + ".ftl"));
allTemplates.put(FreemarkerTemplate.REC_RECOMMEND, configuration.getTemplate(FreemarkerTemplate.REC_RECOMMEND + ".ftl"));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private WordGeneratorUtil() {
throw new AssertionError();
}
/**
* establish doc file
* dataMap data , A place holder for the corresponding template is required , Otherwise it will go wrong
* @param dataMap data
* @param wordName word The name of the report
* @param freemarkerTemplateName Specify which freemarker Templates
* @return
*/
public static File createDoc(String freemarkerTemplateName, String wordName, Map<String, String> dataMap) {
try {
File f = new File(wordName);
Template t = allTemplates.get(freemarkerTemplateName);
// This place can't be used FileWriter Because you need to specify the encoding type, otherwise generated Word The document cannot be opened because of an unrecognized encoding
Writer w = new OutputStreamWriter(new FileOutputStream(f), StandardCharsets.UTF_8);
t.process(dataMap, w);
w.close();
return f;
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(" Generate word Document failed ");
}
}
}
Call layer :
- At the business level , Will need to export the data , According to the place holder i Information assignment , Be careful not to miss , Otherwise, the exported file will not open
@Override
public File exportQualityStep4Word(WordReportDTO exportWordRequest) {
Map<String, String> datas = new HashMap(QualityConstants.HASH_MAP_INIT_VALUE);
// Main title
datas.put("schoolName", exportWordRequest.getSchoolName());
datas.put("title1", exportWordRequest.getBaseSituation());
datas.put("title2", exportWordRequest.getLearningEnvRec());
datas.put("title3", exportWordRequest.getLearningEnvPro());
datas.put("title4", exportWordRequest.getDayLifeRec());
datas.put("title5", exportWordRequest.getDayLifePro());
datas.put("title6", exportWordRequest.getLearningActivityRec());
datas.put("title7", exportWordRequest.getLearningActivityPro());
datas.put("title8", exportWordRequest.getDevRecommend());
datas.put("base64_1", exportWordRequest.getBase64_1());
datas.put("base64_2", exportWordRequest.getBase64_2());
datas.put("base64_3", exportWordRequest.getBase64_3());
datas.put("base64_4", exportWordRequest.getBase64_4());
datas.put("base64_5", exportWordRequest.getBase64_5());
datas.put("base64_6", exportWordRequest.getBase64_6());
// export
return WordGeneratorUtil.createDoc(WordGeneratorUtil.FreemarkerTemplate.REPORT,
exportWordRequest.getWordName(),
datas);
}
- The following is the basic operation of generating report export , You can copy the past changes where they are used
/**
* Export report operation for generating report
*
* @param request request
* @param response The response data
* @param exportWordRequest export dto
*/
@PostMapping("/quality/exportword")
@ResponseBody
public void povertyExportWord(HttpServletRequest request, HttpServletResponse response,
WordReportDTO exportWordRequest) {
File file = qualityReportService.exportQualityStep4Word(exportWordRequest);
InputStream fin = null;
OutputStream out = null;
try {
// Call the tool class WordGeneratorUtils Of createDoc Method generation Word file
fin = new FileInputStream(file);
response.setCharacterEncoding(QualityConstants.UTF_8);
response.setContentType(QualityConstants.CONTENT_TYPE_WORD);
// Set up the browser to process the file as a download
// Set the file name encoding to solve the problem of file name garbled
// Get the... In the request header User-Agent
String filename = exportWordRequest.getWordName();
String agent = request.getHeader(QualityConstants.USER_AGENT);
String filenameEncoder = "";
// Make different judgments according to different browsers
if (agent.contains(QualityConstants.MSIE)) {
// IE browser
filenameEncoder = URLEncoder.encode(filename, QualityConstants.UTF_8);
filenameEncoder = filenameEncoder.replace("+", " ");
} else if (agent.contains(QualityConstants.FIREFOX)) {
// Firefox
BASE64Encoder base64Encoder = new BASE64Encoder();
filenameEncoder = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes(QualityConstants.UTF_8)) + "?=";
} else {
// Other browsers
filenameEncoder = URLEncoder.encode(filename, QualityConstants.UTF_8);
}
response.setHeader(QualityConstants.ACCESS_CONTROL_ALLOW_ORIGIN, "*");// All domains can span
response.setHeader(QualityConstants.CONTENT_TYPE, QualityConstants.CONTENT_TYPE_STEAM);// Binary system Streaming files
response.setHeader(QualityConstants.CONTENT_DISPOSITION, "attachment;filename=" + filenameEncoder + ".doc");// Download and its file name
response.setHeader(QualityConstants.CONNECTION, QualityConstants.CLOSE);// Request to close the connection head
// Set the file to open or download in the browser
response.setContentType(QualityConstants.CONTENT_TYPE_DOWNLOAD);
out = response.getOutputStream();
byte[] buffer = new byte[QualityConstants.BYTE_512];
int bytesToRead = QualityConstants.NUM_MINUS_1;
// Will read in... Through the loop Word The contents of the file are output to the browser
while ((bytesToRead = fin.read(buffer)) != QualityConstants.NUM_MINUS_1) {
out.write(buffer, QualityConstants.NUM_ZERO, bytesToRead);
}
} catch (Exception e) {
throw new RuntimeException(QualityConstants.FARIURE_EXPORT, e);
} finally {
try {
if (fin != null) {
fin.close();
}
if (out != null) {
out.close();
}
if (file != null) {
file.delete();
}
} catch (IOException e) {
throw new RuntimeException(QualityConstants.FARIURE_EXPORT, e);
}
}
}
Export entities dto
Here's an exported entity dto, Entity objects can be customized :
package com.zxd.interview.dto;
/**
* Test using dto, Used to encapsulate exports word The object of
*
* @author zhaoxudong
* @version 1.0
* @date 2020/11/7 23:37
*/
public class TestReportDTO {
/**
* test
*/
private String test0;
/**
* test
*/
private String test1;
/**
* test
*/
private String test2;
/**
* test
*/
private String test4;
/**
* test
*/
private String test5;
/**
* test
*/
private String test6;
/**
* Report name
*/
private String wordName;
public String getTest0() {
return test0;
}
public void setTest0(String test0) {
this.test0 = test0;
}
public String getTest1() {
return test1;
}
public void setTest1(String test1) {
this.test1 = test1;
}
public String getTest2() {
return test2;
}
public void setTest2(String test2) {
this.test2 = test2;
}
public String getTest4() {
return test4;
}
public void setTest4(String test4) {
this.test4 = test4;
}
public String getTest5() {
return test5;
}
public void setTest5(String test5) {
this.test5 = test5;
}
public String getTest6() {
return test6;
}
public void setTest6(String test6) {
this.test6 = test6;
}
public String getWordName() {
return wordName;
}
public void setWordName(String wordName) {
this.wordName = wordName;
}
@Override
public String toString() {
return "TestReportDTO{" +
"test0='" + test0 + '\'' +
", test1='" + test1 + '\'' +
", test2='" + test2 + '\'' +
", test4='" + test4 + '\'' +
", test5='" + test5 + '\'' +
", test6='" + test6 + '\'' +
'}';
}
}
Constant configuration module :
I don't like hard coding , Ugly and ugly , So a lot of things will be replaced by immutable objects .
package com.zxd.interview.constant;
/**
* Constant configuration class
*
* @author zhouhui
*/
public class QualityConstants {
/**
* Quality inspection Supervision of the project id
*/
public static final int EVENTID = 12;
/**
* Numbers 0
*/
public static final int NUM_ZERO = 0;
/**
* Numbers 1
*/
public static final int NUM_ONE = 1;
/**
* Numbers 2
*/
public static final int NUM_TWO = 2;
/**
* Numbers -1
*/
public static final int NUM_MINUS_1 = -1;
/**
* Byte size 512
*/
public static final int BYTE_512 = 512;
/**
* 500 Error code
*/
public static final int CODE_500 = 500;
/**
* 500 Error message - Illegal state
*/
public static final String CODE_500_MSG_1 = " Illegal state !";
/**
* 500 Error message - Unsupervised users are not allowed to view quality inspection records
*/
public static final String CODE_500_MSG_2 = " Unsupervised users are not allowed to view quality inspection records !";
/**
* 500 Error message - This quality monitoring has been completed ! Can't modify
*/
public static final String CODE_500_MSG_3 = " This quality monitoring has been completed ! Can't modify !";
/**
* 500 Error message - Submit failed , Material upload cannot be empty
*/
public static final String CODE_500_MSG_4 = " Submit failed , Material upload cannot be empty ";
/**
* 500 Error message - Submit failed , Please try again later or contact the Administrator
*/
public static final String CODE_500_MSG_5 = " Submit failed , Please try again later or contact the Administrator !";
/**
* 500 Error message - Submit failed , Feedback cannot be empty
*/
public static final String CODE_500_MSG_6 = " Submit failed , Feedback cannot be empty !";
/**
* 405 Error code
*/
public static final int CODE_405 = 405;
/**
* 405 Error message - This information can only be viewed by the supervisor
*/
public static final String CODE_405_MSG_1 = " This information can only be viewed by the supervisor !";
/**
* 200 Successfully coded
*/
public static final int CODE_200 = 200;
/**
* 200 Success message - This information can only be viewed by the supervisor
*/
public static final String CODE_200_MSG_1 = " Submit successfully !";
/**
* Error message - No record has been selected
*/
public static final String DELETE_FAIRURE_MSG = " Delete failed , No record has been selected !";
/**
* Error message - No record has been selected
*/
public static final String NO_RECORD_SELECTED = " No record has been selected !";
/**
* Character encoding utf-8
*/
public static final String UTF_8 = "utf-8";
/**
* Default pid
*/
public static final int PID = 0;
/**
* Default level
*/
public static final int DEFUALT_LAYER = 1;
/**
* Inappropriate lowest score
*/
public static final Integer MIN_SCORE = 1;
/**
* Excellent, highest score
*/
public static final Integer MAX_SCORE = 7;
/**
* map Of hash Initial value
*/
public static final int HASH_MAP_INIT_VALUE = 32;
/**
* The average score of the whole garden is
*/
public final static String WHOLE_AVERAGE = " The average score of the whole garden is ";
/**
* The query fails
*/
public final static String QUERY_FAIRURE = " The query fails ";
/**
* Successful operation
*/
public final static String SUCCESS_MSG = " Successful operation !";
/**
* operation failed
*/
public final static String FARIURE_MSG = " operation failed !";
/**
* Export failed
*/
public final static String FARIURE_EXPORT = " Export failed !";
/**
* Request header - file
*/
public final static String CONTENT_TYPE_WORD = "application/msword";
/**
* Request header - download
*/
public final static String CONTENT_TYPE_DOWNLOAD = "application/x-download";
/**
* Request header - Binary
*/
public final static String CONTENT_TYPE_STEAM = "application/octet-stream;charset=UTF-8";
/**
* Request header
*/
public final static String USER_AGENT = "User-Agent";
/**
* Request header
*/
public final static String CONTENT_TYPE = "Content-Type";
/**
* Connect
*/
public final static String CONNECTION = "Connection";
/**
* Close the connection
*/
public final static String CLOSE = "close";
/**
* Connect
*/
public final static String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
/**
* Connect
*/
public final static String CONTENT_DISPOSITION = "Content-Disposition";
/**
* browser - ie
*/
public final static String MSIE = "MSIE";
/**
* browser - Firefox
*/
public final static String FIREFOX = "Firefox";
/**
* Fill in the report step
*/
public final static String MODULE_STEP3_REPORT = "qualityreport";
/**
* Supervise the verification materials of the kindergarten
*/
public final static String MODULE_STEP1_MATERIAL = "qualitymetrail";
/**
* Numbers 3
*/
public final static int NUM_3 = 3;
/**
* Numbers 4
*/
public final static int NUM_4 = 4;
/**
* Numbers 5
*/
public final static int NUM_5 = 5;
/**
* Numbers 6
*/
public final static int NUM_6 = 6;
/**
* Numbers 7
*/
public final static int NUM_7 = 7;
/**
* Numbers 8
*/
public final static int NUM_8 = 8;
/**
* Numbers 9
*/
public final static int NUM_9 = 9;
/**
* Numbers 10
*/
public final static int NUM_10 = 10;
/**
* Numbers 11
*/
public final static int NUM_11 = 11;
/**
* Numbers 12
*/
public final static int NUM_12 = 12;
/**
* Numbers 13
*/
public final static int NUM_13 = 13;
/**
* Numbers 14
*/
public final static int NUM_14 = 14;
/**
* Numbers 15
*/
public final static int NUM_15 = 15;
/**
* Numbers 16
*/
public final static int NUM_16 = 16;
/**
* Numbers 17
*/
public final static int NUM_17 = 17;
/**
* Numbers 18
*/
public final static int NUM_18 = 18;
/**
* Numbers 19
*/
public final static int NUM_19 = 19;
/**
* Numbers 20
*/
public final static int NUM_20 = 20;
/**
* Formatting Numbers
*/
public final static String DECIMAL_Format = "######.00";
}
Page layer processing :
Add a... To the front end form Submit , Use form Submit form data , Realization word Export function :
( Note that the template engine used is thymeleaf)
html Code :
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<!-- Latest version Bootstrap The core CSS file -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="container">
<div id="vue1" class="row">
<!-- <form @submit.prevent="submit">-->
<!-- <div>-->
<!-- export word name :<input class="form-control" type="text" v-model="student.wordName">-->
<!-- </div>-->
<!-- <div>-->
<!-- test0:<input type="text" class="form-control" v-model="student.test0">-->
<!-- test1:<input type="text" class="form-control" v-model="student.test1">-->
<!-- test2:<input type="text" class="form-control" v-model="student.test2">-->
<!-- test4:<input type="text" class="form-control" v-model="student.test4">-->
<!-- test5:<input type="text" class="form-control" v-model="student.test5">-->
<!-- test6:<input type="text" class="form-control" v-model="student.test6">-->
<!-- </div>-->
<!-- <input type="submit" class="btn btn-danger" value=" Submit ">-->
<!-- </form>-->
<form method="post" action="/quality/exportword">
<div>
export word name :<input class="form-control" type="text" name="wordName">
</div>
<div>
test0:<input type="text" class="form-control" name="test0">
test1:<input type="text" class="form-control" name="test1">
test2:<input type="text" class="form-control" name="test2">
test4:<input type="text" class="form-control" name="test4">
test5:<input type="text" class="form-control" name="test5">
test6:<input type="text" class="form-control" name="test6">
</div>
<input type="submit" class="btn btn-danger" value=" Submit ">
</form>
</div>
</div>
</body>
<!-- Development environment version , Contains helpful command-line warnings -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.staticfile.org/vue-resource/1.5.1/vue-resource.min.js"></script>
<script th:src="@{/js/exportword.js}"></script>
</html>
js Code
Use js Code processing form Form submission , Used jquery Export , In fact, I don't know how to export the binary stream generated by the background , There are many ways to do it , Next time I write an article, I'll summarize some usages .
var v1 = new Vue({
el: '#vue1',
data: {
counter: 0,
student: {
test0:'',
test1:'',
test2:'',
test3:'',
test4:'',
test5:'',
test6:'',
wordName: '',
}
},
methods: {
test: function () {
console.log(this.counter);
},
submit() {
console.log(this.student);
var url = '/quality/exportword';
var formData = JSON.stringify(this.student); // this Point to this VUE example data The default binding is under instance . So direct this.student It's the data to be submitted
this.$http.post(url, formData).then(function (data) {
console.log(data);
let blob = new Blob([data.data],{ type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=utf-8'});
let objectUrl = URL.createObjectURL(blob);
window.location.href = objectUrl;
}).catch(function () {
console.log('test');
});
}
}
})
ending :
Average personal level , I hope this article can help readers , You are welcome to point out the mistakes , When you see it, it will be changed to , thank you !
Some time ago, I was busy interviewing and found a new place to work , When the job is stable , Will continue to dig deep into blogs and technology stacks .