项目中有这样的需求,将数据库中的查询结果导出成json格式文件,于是我总结了三种实现方式:
public void exportTestHisDetail(JSONObject condition , HttpServletResponse response) {
// 使用mybatisPlus查询数据结果
String key= condition.getString("key");
LambdaQueryWrapper<TestHisDetail> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TestHisDetail::getTestBatchKey,key);
// list是从数据库中查询出的结果集
List<TestHisDetail> list = detail.selectList(queryWrapper);
// 导出内容到浏览器
try (
// 获取响应输出流
OutputStream outputStream = response.getOutputStream();){
// 设置响应头
response.setContentType("application/json");
response.setHeader("Content-Disposition", "attachment; filename=\"export.json\"");
// 将查询到的结果序列化为json格式
String jsonString = JSON.toJSONString(list, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteDateUseDateFormat);
// 将序列化的json数据集写入到输出流中
outputStream.write(jsonString.getBytes(StandardCharsets.UTF_8));
// 推送输出流结果到浏览器
outputStream.flush();
}catch (Exception e){
log.error("导出文件失败"+e);
}
}
在上面的代码中,我们同样使用 JdbcTemplate 执行 SQL 查询,获取结果集。然后,我们设置 HTTP 响应的 Content-Type 为 application/json,以及 Content-Disposition 头,将文件名设置为 export.json。
接下来,我们使用 fastjson 将结果集转换为 JSON 字符串,并设置了一些序列化特性,例如 PrettyFormat(格式化输出)、WriteMapNullValue(输出 null 值的属性)、WriteDateUseDateFormat(按照指定的日期格式输出日期)。最后,我们将 JSON 字符串写入到输出流中,并关闭输出流。
需要注意的是,fastjson 可能存在一些安全问题,因此建议使用时选择较新版本的 fastjson,并及时更新以修复已知漏洞。另外,需要根据具体的业务需求和查询结果的特点,设置合适的序列化特性,以避免导出的文件过大或格式不正确等问题。
public void exportTestHisDetail(JSONObject condition,HttpServletResponse response){
// 使用mybatisPlus查询数据结果
String key= condition.getString("key");
LambdaQueryWrapper<TestHisDetail> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TestHisDetail::getTestBatchKey,key);
// list是从数据库中查询出的结果集
List<TestHisDetail> list = detail.selectList(queryWrapper);
// 设置响应头
response.setContentType("application/json");
response.setHeader("Content-Disposition", "attachment; filename=\"export.json\"");
try(
// 获取响应输出流
OutputStream outputStream = response.getOutputStream();
){
// 使用springboot的Jackson库进行序列化
ObjectMapper mapper = new ObjectMapper();
mapper.writerWithDefaultPrettyPrinter().writeValue(outputStream , list);
}catch (Exception e){
log.error("导出文件失败"+e);
}
}
在上面的代码中,我们首先执行数据库查询,获取结果集。然后,我们设置HTTP响应的Content-Type为application/json,以及Content-Disposition头,将文件名设置为export.json。接下来,我们使用ObjectMapper将结果集写入到输出流中,并使用writerWithDefaultPrettyPrinter方法设置缩进格式,使输出结果更易读。最后,我们关闭输出流。
需要注意的是,如果导出的数据量比较大,可能会导致内存占用过高,从而导致性能问题。此时,可以考虑使用分页查询或者流式查询来解决这个问题。另外,还需要注意设置合适的Content-Type和Content-Disposition头,以确保浏览器能够正确地识别导出的文件类型和文件名。
在controller层以ResponseEntity作为返回值,返回给浏览器端
// 为了精简代码此方法就直接抛出异常,不再进行抓取
@GetMapping("/export")
public ResponseEntity<Resource> exportTestHisDetail() throws IOException {
// TODO: 查询数据库并导出数据为 JSON 字符串,此处可参考上面的查询案例
String jsonData = ...;
// 构造 ResponseEntity 对象,设置响应头和响应体
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=export.json");
// 设置响应体为json格式
MediaType mediaType = MediaType.APPLICATION_JSON;
ResponseEntity<String> responseEntity = ResponseEntity.ok()
.headers(headers)
.contentType(mediaType)
.body(jsonData);
return responseEntity;
在上述代码中,我们首先查询数据库并将查询结果导出为一个 JSON 字符串。然后,我们使用 ResponseEntity 对象来构造响应,将导出的 JSON 字符串设置为响应体,并设置响应头中的 Content-Disposition 为 “attachment; filename=export.json”,表示将响应体作为附件下载,并将下载的文件命名为 export.json。
最后,我们使用 ResponseEntity.ok() 方法来设置响应状态码为 200 OK,并返回 ResponseEntity 对象。这样,在调用该 RESTful API 接口时,服务器会将导出的 JSON 数据作为一个名为 export.json 的附件返回给客户端,客户端就可以将其下载保存到本地磁盘中。
以下是文件下载中的几个细节:
String json = JSON.toJSONString(object,
// 控制是否将输出的 JSON 字符串格式化为可读性更好的形式
SerializerFeature.PrettyFormat,
// 控制是否将 Map 中的空值输出为 null 值
SerializerFeature.WriteMapNullValue,
// 控制是否将 null 值的字符串输出为空字符串
SerializerFeature.WriteNullStringAsEmpty,
// 控制是否将 null 值的数字类型输出为 0
SerializerFeature.WriteNullNumberAsZero,
// 控制是否禁用循环引用检测
SerializerFeature.DisableCircularReferenceDetect,
// 控制是否将枚举类型输出为字符串
SerializerFeature.WriteEnumUsingToString
);
常用的 Content-Disposition 值有两个:
inline:将响应体作为普通的网页内容直接在浏览器中展示;
attachment:将响应体作为附件下载到本地。
当设置 Content-Disposition 为 attachment 时,可以使用 filename 参数指定下载文件的文件名,例如:
Content-Disposition: attachment; filename="export.json"
这样就会在下载对话框中显示一个默认文件名为 “export.json” 的下载链接,用户点击下载后会将服务器返回的响应体保存为一个名为 “export.json” 的文件。
需要注意的是,由于 Content-Disposition 是一个响应头部字段,因此只能由服务器端设置。客户端无法直接修改响应头部字段。
ResponseEntity 的构造函数接受一个泛型类型参数,用于指定响应体的类型。例如,可以使用
ResponseEntity<String> responseEntity = ResponseEntity.ok().body("Hello, world!");
来构造一个包含字符串 “Hello, world!” 的 HTTP 响应,其中的 ok() 方法表示响应状态码为 200 OK。
在实际使用中,我们可以使用 ResponseEntity 对象来返回各种类型的响应,包括文本、HTML、JSON、XML 等格式的数据。同时,我们还可以设置响应头、响应状态码等属性,以便更好地控制 HTTP 响应的行为。