使用POI XWPF生成Word文檔,引入POI:java
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.0</version> </dependency>
項目中常常從Word模板生成文檔,下面示例演示了替換文檔內容的方法。模版中要替換的內容以${}標識,調用XWPFRun.setText()方法更新文檔。node
import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.util.List; import java.util.Map; public final class XWPFDocumentUtils { private XWPFDocumentUtils() { } public static byte[] replaceDocument(String path, Map<String, String> fields) throws IOException { try (XWPFDocument doc = new XWPFDocument(new FileInputStream(path))) { for (XWPFParagraph paragraph : doc.getParagraphs()) { if (!paragraph.getText().contains("${")) { continue; } replaceParagraph(paragraph, fields); } try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { doc.write(out); return out.toByteArray(); } } } private static void replaceParagraph(XWPFParagraph paragraph, Map<String, String> fields) { for (Map.Entry<String, String> field : fields.entrySet()) { String find = "${" + field.getKey() + "}"; if (!paragraph.getText().contains(find)) { continue; } replaceText(paragraph, find, field.getValue()); } } private static void replaceText(XWPFParagraph paragraph, String key, String value) { List<XWPFRun> runs = paragraph.getRuns(); for (int i = 0; i < runs.size(); i++) { XWPFRun run = runs.get(i); String text = run.text(); if (text.contains("${") || (text.contains("$") && runs.get(i + 1).text().startsWith("{"))) { StringBuilder builder = new StringBuilder(text); while (!text.contains("}")) { text = runs.get(i + 1).text(); builder.append(text); paragraph.removeRun(i + 1); } text = builder.toString(); run.setText(text.contains(key) ? text.replace(key, value) : text, 0); } } } }
調用replaceDocument()方法生成word文檔,如要在Rest API中定義文件名稱,使用ResponseEntity並增長header,不然能夠直接返回byte[]。mysql
@GetMapping("/api/doc/{heroName}") public ResponseEntity<byte[]> getDocument(@PathVariable String heroName) { try { Map<String, String> fields = new HashMap<>(); fields.put("hero_name", heroName); fields.put("create_date", "2019年6月"); byte[] bytes = XWPFDocumentUtil.replaceDocument("template/hero.docx", fields); HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition", "attachment;filename=hero.docx"); return ResponseEntity.ok().headers(headers).body(bytes); } catch (Exception e) { throw new XWPFDocumentException(e.getMessage()); } }
配置CORS的ExposedHeaders,不然前臺不能讀取"Content-Disposition":web
@Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); SecurityProperties.Cors cors = config.getCors(); configuration.setAllowedMethods(Arrays.asList("*")); configuration.setAllowedHeaders(Arrays.asList("Accept","Accept-Encoding","Accept-Language","Authorization","Connection","Content-Type","Host","Origin","Referer","User-Agent","X-Requested-With")); configuration.setExposedHeaders(Arrays.asList("Content-Disposition")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; }
測試使用exchange方法,設置header APPLICATION_OCTET_STREAM:spring
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.*; import org.springframework.test.context.junit4.SpringRunner; import java.util.Arrays; import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class HeroesApplicationTests { @Autowired private TestRestTemplate restTemplate; @Test public void getDocumentSuccess() { HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM)); HttpEntity<String> entity = new HttpEntity<>(headers); ResponseEntity<byte[]> response = restTemplate.exchange("/api/doc/jason", HttpMethod.GET, entity, byte[].class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); } }
可使用連接直接訪問REST URL下載文檔,若項目啓用了JWT Token驗證,則必須使用HttpClient的get方法。
本文使用了FileSaver.js保存文檔,開始以前先安裝:sql
npm install --save file-saver
而後在tsconfig.json中添加:apache
"paths": { "file-saver": [ "node_modules/file-saver/dist/FileSaver.js" ] }
下載方法:npm
import * as fs from 'file-saver'; downloadDocument() { this.httpClient.get('yourUrl', {observe: 'response', responseType: 'blob'}).subscribe(response => { fs.saveAs(response.body, this.getFilename(response.headers)); }); } private getFilename(headers: HttpHeaders): string { const disposition = headers.get('Content-Disposition'); if (!disposition || disposition.indexOf('filename=') < 0) { return ''; } return disposition.substr(disposition.indexOf('filename=') + 9); }
或json
downloadDocument() { this.httpClient.get('yourUrl', {responseType: 'blob'}).subscribe(data => { fs.saveAs(data, 'yourFilename'); }); }
Excel File – Download from SpringBoot RestAPI + Apache POI + MySQL
Apache POI Word Tutorialapi