Angular/Spring Boot Rest API downloads Word documents
POI generates Word documents
Use POI XWPF to generate Word documents and introduce POI:
Org.apache.poi poi-ooxml 4.1.0
Documents are often generated from Word templates in a project, and the following example demonstrates how to replace the contents of a document. The content to be replaced in the template is identified by ${}, and the XWPFRun.setText () method is called to update the document.
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 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 fields) {for (Map.Entry 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 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); } } }}Spring Boot Rest APIRest API 调用replaceDocument()方法生成word文档,如要在Rest API中定义文件名称,使用ResponseEntity并增加header,否则可以直接返回byte[]。 @GetMapping("/api/doc/{heroName}")public ResponseEntity getDocument(@PathVariable String heroName) { try { Map 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 配置CORS的ExposedHeaders,否则前台不能读取"Content-Disposition": @BeanCorsConfigurationSource 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;}Test 测试使用exchange方法,设置header APPLICATION_OCTET_STREAM: 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 entity = new HttpEntity(headers); ResponseEntity response = restTemplate.exchange("/api/doc/jason", HttpMethod.GET, entity, byte[].class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); }}Angular下载文档 可以使用链接直接访问REST URL下载文档,若项目启用了JWT Token验证,则必须使用HttpClient的get方法。 本文使用了FileSaver.js保存文档,开始之前先安装: npm install --save file-saver 然后在tsconfig.json中添加: "paths": { "file-saver": [ "node_modules/file-saver/dist/FileSaver.js" ]} 下载方法: 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);} 或 downloadDocument() { this.httpClient.get('yourUrl', {responseType: 'blob'}).subscribe(data =>{fs.saveAs (data, 'yourFilename');});} reference documentation
Excel File-Download from SpringBoot RestAPI + Apache POI + MySQL
Apache POI Word Tutorial