目录
- 需求
- 一、准备模板文件
- 二、引入Poi-tl、Apache POI依赖
- 三、创建实体类(用于保存向Word中写入的数据)
- 四、实现Service接口
- 五、Controller层实现
需求
在服务端提前准备好Word模板文件,并在用户请求接口时服务端动态获取图片。数据等信息插入到模板当中,然后返回包含数据信息的Word文件流。
一、准备模板文件
在需要插入图片的地方使用:{{@参数名}},文本信息使用:{{参数名}},进行占位,占位格式将会被保留,经过处理后格式不变
将准备好的模板文件放在resources目录下
二、引入Poi-tl、Apache POI依赖
poi-tl(poi template language)是Word模板引擎,基于Apache POI,提供更友好的API,使用起来更加简单
版本对应关系参考Poi-tl官网
<!-- 替换自己使用的版本 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.*</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.*</version>
</dependency>
<!-- Word模板引擎 -->
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.7.*</version>
</dependency>
三、创建实体类(用于保存向Word中写入的数据)
参数名必须同Word模板中的参数名称保持一致
import com.deepoove.poi.data.PictureRenderData;
@Data
public class DownloadDate {
//图片使用PictureRenderData类型
private PictureRenderData image;
private String name;
private String a;
private String b;
private String c;
private String d;
private String e;
private String f;
private String g;
private String h;
private String i;
}
四、实现Service接口
public interface DownloadService {
void download(HttpServletResponse response, DownloadDTO downloadDTO) throws IOException;
}
@Service
@Slf4j
public class DownloadServiceImpl implements DownloadService {
@Resource
//远程调用服务
private FeignService feignService;
@Override
public void download(HttpServletResponse response, DownloadDTO downloadDTO) throws IOException {BufferedImage、字节数组),Base64可以转字节数组后使用
//通过调用其它接口获取待写入的数据信息
WordData wordData = feignService.getData(downloadDTO);
/**
* 图片可以是多种格式------------------------
* 图片路径:PictureRenderData(int width, int height, String path)
* File:PictureRenderData(int width, int height, File picture)
* InputStream:PictureRenderData(int width, int height, String format, InputStream input)
* BufferedImage:PictureRenderData(int width, int height, String format, BufferedImage image)
* 字节数组:PictureRenderData(int width, int height, String format, byte[] data)
* Base64可以转字节数组后使用
*/
//以Base64为例,先获取图片的Base64编码(wordData.getImg是原始图片Base64数据)
String base64ImageData = wordData.getImg.substring(data.indexOf(",") + 1);
//获取图片类型
String format = getBase64Type(base64ImageData);
// 将base64数据转为字节数组
byte[] imageBytes = Base64.getDecoder().decode(base64ImageData);
// 将字节数组包装成PictureRenderData
PictureRenderData pictureRenderData = new PictureRenderData(690,530,format,imageBytes);
//待写入Word的数据
DownloadDate downloadDate = new DownloadDate();
//图片信息
downloadDate.setImage(pictureRenderData);
//其它信息
downloadDate.setName(wordData.getName());
//...
XWPFTemplate template = null;
BufferedOutputStream bufferedOutputStream = null;
ServletOutputStream outputStream = null;
try {
/**
* 该方法会导致在部分环境中资源找不到的情况,不推荐使用
*/
//获得resource路径+模板路径
//String path = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("")).getPath() + "word/template.docx";
// 读取Word模板
//FileInputStream templateInputStream = new FileInputStream(path);
// 模板绑定数据
//template = XWPFTemplate.compile(templateInputStream).render(imageDownloadDate);
// 从资源中加载Word模板
try (InputStream templateInputStream = getClass().getClassLoader().getResourceAsStream("word/template.docx")) {
if (templateInputStream != null) {
// 模板绑定数据
template = XWPFTemplate.compile(templateInputStream).render(imageDownloadDate);
} else {
// 处理模板资源未找到的情况
log.error("Word模板资源未找到");
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
}
//文件名
String encodedFileName = URLEncoder.encode(System.currentTimeMillis()+"", "utf-8");
//设置响应信息
response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName + ".docx");
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
response.setCharacterEncoding("utf-8");
outputStream = response.getOutputStream();
bufferedOutputStream = new BufferedOutputStream(outputStream);
template.write(bufferedOutputStream);
//清空流
bufferedOutputStream.flush();
outputStream.flush();
} catch (Exception e) {
log.info(e.getMessage());
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} finally {
//关闭资源
PoitlIOUtils.closeQuietlyMulti(template, bufferedOutputStream, outputStream);
}
}
//根据base64编码获取图片格式信息
private String getBase64Type(String base64) {
byte[] b = Base64.getDecoder().decode(base64);
String type = ".png";
if (0x424D == ((b[0] & 0xff) << 8 | (b[1] & 0xff))) {
type = ".bmp";
} else if (0x8950 == ((b[0] & 0xff) << 8 | (b[1] & 0xff))) {
type = ".png";
} else if (0xFFD8 == ((b[0] & 0xff) << 8 | (b[1] & 0xff))) {
type = ".jpg";
} else if (0x49492A00 == ((b[0] & 0xff) << 24 | (b[1] & 0xff) << 16 | (b[2] & 0xff) << 8 | (b[3] & 0xff))) {
type = ".tif";
}
return type;
}
}
五、Controller层实现
@RestController
@RequestMapping("/test")
@Api(tags = "获取商品图片")
public class GetImageController {
@Resource
DownloadService downloadService;
@PostMapping("/download")
@ApiOperation(value = "下载Word")
void download(HttpServletResponse response,@RequestBody DownloadDTO downloadDTO) throws IOException {
//鉴权或其它处理
//....
downloadService.download(response,downloadDTO);
}
}