Java实现动态生成word报告

1.准备好docx文件模板

举例:动态生成表格数据,以下是list数组类型的freemarker语法

将写好的word模板加入到templates目录下

2.在pom.xml中导入相关依赖

<dependency><groupId>fr.opensagres.xdocreport</groupId><artifactId>fr.opensagres.xdocreport.document.docx</artifactId><version>2.0.3</version></dependency><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.1</version></dependency>

3.关于Minio的介绍

Minio 是一个开源的对象存储服务器,可用于存储和访问海量数据。以下是 Minio 的基本用法:

(1)安装 Minio

首先,你需要下载和安装 Minio。你可以从 Minio 的官方网站(https://min.io/)下载适用于你的操作系统的安装包,然后按照 Minio 的安装说明进行操作。

(2)启动 Minio

安装完成后,你可以通过运行以下命令来启动 Minio:

bash复制代码minio server /path/to/data

其中 /path/to/data 是你用于存储数据的目录或磁盘空间。

(3)访问 Minio

一旦 Minio 启动成功,你就可以使用 Web 浏览器、命令行工具或 SDK 来访问和操作 Minio。

  • Web 浏览器:在浏览器中输入 Minio 的访问地址(默认为 localhost:9000),即可看到 Minio 的管理界面。你可以使用默认的用户名和密码(分别为 miniominio123)登录。
  • 命令行工具:你可以使用 Minio 提供的命令行工具(如 mcbs)来访问和操作 Minio。这些工具提供了丰富的命令行选项和 SDK,方便你进行数据的上传、下载、备份、恢复等操作。
  • SDK:Minio 还提供了多种编程语言的 SDK,如 Java、Python、Go、JavaScript 等。你可以使用这些 SDK 在应用程序中集成 Minio 的功能,以便更方便地管理和操作数据。

总之,使用 Minio 可以通过简单的安装和配置,快速构建一个可靠的对象存储服务,方便你存储和访问海量数据。

4.在application.yml中添加Minio配置

minio://host和port自行填写endpoint: http://127.0.0.1:9000accessKey: miniosecretKey: minio123bucketName: carbonexpires: 86400

5.导入MinioConfig配置类

@Data@Configurationpublic class MinioConfig {@Value("${minio.endpoint}")private String endpoint;@Value("${minio.bucketName}")private String bucketName;@Value("${minio.accessKey}")private String accessKey;@Value("${minio.secretKey}")private String secretKey;@Value("${minio.expires}")private Integer expires = 86400;}

6.导入UploadFileService类与其实现类UploadFileServiceImpl(直接CV过去)

public interface UploadFileService {void createBucket(String bucketName);/** * 获取全部bucket * 

* https://docs.minio.io/cn/java-client-api-reference.html#listBuckets */List<Bucket> getAllBuckets();/** * 根据bucketName获取信息 * * @param bucketName bucket名称 */// @SneakyThrowsOptional<Bucket> getBucket(String bucketName);/** * 根据bucketName删除信息 * * @param bucketName bucket名称 */void removeBucket(String bucketName);/** * 根据文件前置查询文件 * * @param bucketName bucket名称 * @param prefix 前缀 * @param recursive是否递归查询 * @return MinioItem 列表 */List getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive);/** * 获取文件外链 * * @param bucketName bucket名称 * @param objectName 文件名称 * @param expires过期时间 <=7 * @return url */String getObjectURL(String bucketName, String objectName, Integer expires);/** * 获取文件 * * @param bucketName bucket名称 * @param objectName 文件名称 * @return 二进制流 */InputStream getObject(String bucketName, String objectName);/** * 上传文件 * * @param bucketName bucket名称 * @param stream 文件流 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject */void putObject(String bucketName, String objectName, InputStream stream);/** * 上传文件 * * @param bucketNamebucket名称 * @param objectName文件名称 * @param stream文件流 * @param size大小 * @param contextType 类型 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject */void putObject(String bucketName, String objectName, InputStream stream, long size, String contextType);/** * 获取文件信息, 如果抛出异常则说明文件不存在 * * @param bucketName bucket名称 * @param objectName 文件名称 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject */StatObjectResponse getObjectInfo(String bucketName, String objectName);/** * 删除文件 * * @param bucketName bucket名称 * @param objectName 文件名称 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject */boolean removeObject(String bucketName, String objectName) throws Exception;}

@Slf4j@Servicepublic class UploadFileServiceImpl implements UploadFileService {private MinioClient minioClient;@AutowiredMinioConfig minioConfig;@PostConstructpublic void init() {this.minioClient =MinioClient.builder().endpoint(minioConfig.getEndpoint()).credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey()).build();}@Override@SneakyThrowspublic void createBucket(String bucketName) {if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}}@Override@SneakyThrowspublic List<Bucket> getAllBuckets() {// 列出所有存储桶return minioClient.listBuckets();}@Override@SneakyThrowspublic Optional<Bucket> getBucket(String bucketName) {return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();}@Override@SneakyThrowspublic void removeBucket(String bucketName) {// 删除之前先检查bucketName是否存在。boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());if (found) {// 删除bucketName存储桶,注意,只有存储桶为空时才能删除成功。minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());} else {log.info(bucketName + "does not exist");}}@Override@SneakyThrowspublic List getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {List<Item> list = new ArrayList<>();Iterable<Result<Item>> objectsIterator = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());if (objectsIterator != null) {Iterator<Result<Item>> iterator = objectsIterator.iterator();if (iterator != null) {while (iterator.hasNext()) {Result<Item> result = iterator.next();Item item = result.get();list.add(item);}}}return list;}@Override@SneakyThrowspublic String getObjectURL(String bucketName, String objectName, Integer expires) {return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(objectName).expiry(expires, TimeUnit.DAYS).build());}@Override@SneakyThrowspublic InputStream getObject(String bucketName, String objectName) {return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());}@Override@SneakyThrowspublic void putObject(String bucketName, String objectName, InputStream stream) {// 检查存储桶是否已经存在if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, stream.available(), -1).build());}@Override@SneakyThrowspublic void putObject(String bucketName, String objectName, InputStream stream, long size, String contextType) {minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, size, -1).contentType(contextType).build());}@Override@SneakyThrowspublic StatObjectResponse getObjectInfo(String bucketName, String objectName) {StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());return statObjectResponse;}@Overridepublic boolean removeObject(String bucketName, String objectName) throws Exception {try {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());} catch (Exception e) {return false;}return true;}}

7.编写ReportData类

@Slf4j@Data@Componentpublic class ReportData {//generateReport可加入需要的参数public String generateReport() throws IOException, XDocReportException {//通过freemarker模板引擎加载文档,并缓存到registry中ClassPathResource resource= new ClassPathResource("templates/report.docx");InputStream input = resource.getInputStream();IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Freemarker);//匹配填充字段和填充数据,进行填充IContext context = report.createContext();//获取要放入报告的数据(省略),假设获取到了一个list数组dataList//context.put(" ", )放入相应的数据,可在word模板中动态调用context.put("time", LocalDateTime.now());context.put("dataList",dataList);ByteArrayOutputStream output = new ByteArrayOutputStream();report.process(context, output);ByteArrayInputStream inputStream = new ByteArrayInputStream(output.toByteArray()); String objectName = "Document/"+"报告.docx";uploadFileService.putObject(minioConfig.getBucketName(),objectName, inputStream);return objectName;}}

8.controller层调用

 @RequestMapping(value = "/reportExport", method = RequestMethod.POST)//UserServiceByReportExport中可填写所需参数public void UserServiceByReportExport(HttpServletRequest request, HttpServletResponse response) throws IOException, XDocReportException {// TODO 使用预置的word文档导出String objectName = reportData.generateReport();Optional<InputStream> inputOp = Optional.of(uploadFileService.getObject(minioConfig.getBucketName(), objectName));if (!inputOp.isPresent()) {throw new NullPointerException("报告生成失败");}try (InputStream input1 = inputOp.get(); OutputStream output1 = response.getOutputStream()) {response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);response.addHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode( "报告.docx", "UTF-8"));response.setHeader("Access-Control-Expose-Headers","Content-Disposition");IoUtils.copy(input1, output1);} catch (Exception e) {e.printStackTrace();}}