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 的管理界面。你可以使用默认的用户名和密码(分别为minio
和minio123
)登录。 - 命令行工具:你可以使用 Minio 提供的命令行工具(如
mc
或bs
)来访问和操作 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:9000
accessKey: minio
secretKey: minio123
bucketName: carbon
expires: 86400
5.导入MinioConfig配置类
@Data
@Configuration
public 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
* <p>
* https://docs.minio.io/cn/java-client-api-reference.html#listBuckets
*/
List<Bucket> getAllBuckets();
/**
* 根据bucketName获取信息
*
* @param bucketName bucket名称
*/
// @SneakyThrows
Optional<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 bucketName bucket名称
* @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
@Service
public class UploadFileServiceImpl implements UploadFileService {
private MinioClient minioClient;
@Autowired
MinioConfig minioConfig;
@PostConstruct
public void init() {
this.minioClient =
MinioClient.builder()
.endpoint(minioConfig.getEndpoint())
.credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey())
.build();
}
@Override
@SneakyThrows
public void createBucket(String bucketName) {
if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
}
@Override
@SneakyThrows
public List<Bucket> getAllBuckets() {
// 列出所有存储桶
return minioClient.listBuckets();
}
@Override
@SneakyThrows
public Optional<Bucket> getBucket(String bucketName) {
return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
}
@Override
@SneakyThrows
public 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
@SneakyThrows
public 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
@SneakyThrows
public 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
@SneakyThrows
public InputStream getObject(String bucketName, String objectName) {
return minioClient.getObject(GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
}
@Override
@SneakyThrows
public 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
@SneakyThrows
public 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
@SneakyThrows
public StatObjectResponse getObjectInfo(String bucketName, String objectName) {
StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
return statObjectResponse;
}
@Override
public 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
@Component
public 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();
}
}