项目背景
Springboot2.6 + MongoDB 存储大型 geojson数据
因为部分文件需要随系统保存,直接存储本地路径会导致系统迁移、Docker运行等出现问题,内网系统不能使用外部OSS,内部单独搭建OSS也难以维护,于是采用MongoDB的GridFS存储文件
获取时使用流式传输获取
示例包含导入、删除
后端代码
Controller
@PostMapping("/import")
@RequiresPermissions(PermissionConstant.ADD)
public ResponseData<?> importGeoData(@RequestParam(value = "param1",required = false) String param1, @RequestParam(value = "param2",required = false) String param2,@RequestParam(value = "size",required = false) long size, @RequestParam(value = "file",required = false) MultipartFile file) throws Exception {
exampleUploadRequest uploadRequest = new exampleUploadRequest();
uploadRequest.setParam1(param1);
uploadRequest.setParam2(param2);
uploadRequest.setSize(size);
uploadRequest.setFile(file);
return getResult(fileService.importExampleData(uploadRequest));
}
@Operation(summary = "根据FileId获取文件")
@GetMapping("/getByFileId/{fileId}")
@RequiresPermissions(PermissionConstant.GET)
public ResponseEntity<StreamingResponseBody> getByFileId(@PathVariable("fileId") String fileId) {
return ResponseEntity.ok()
.header("Content-Type", "application/json")
.header("responseType", "custom type")
.body(fileService.getFile(fileId));
}
Entity
//Dto:
@Data
public class ExampleUploadRequest {
private String param1;
private String param2;
MultipartFile file;
private Long size;
}
//实体:
@Data
@Document
@FieldDefaults(level = AccessLevel.PRIVATE)
@Schema(description = "附属文件数据")
public class TbExample extends BaseEntity implements Serializable {
@Schema(description = "fileID")
@Indexed(unique = true)
private String fileID;
@Schema(description = "参数1")
private String param1;
@Schema(description = "参数2")
private String param2;
@Schema(description = "文件大小")
private Long size;
}
ServiceImpl
//------------导入-------------:
@Override
public Boolean importExampleData(exampleUploadRequest uploadRequest) throws IOException {
//存储附属数据
TbExample exampleData = new Tbexample();
exampleData.setId(RandomIdUtil.generateSimpleUuid());
//复制属性
BeanUtils.copyProperties(uploadRequest, exampleData);
exampleData.setCreateTime(LocalDateTime.now());
// 使用 GridFS 保存文件,可以改为获取File的文件名保存(uploadRequest.getFile().getName()))
String fileID = saveFileToGridFS(uploadRequest.getFile(), exampleData.getId() + ".json");
exampleData.setFileID(fileID); // 保存 GridFS 生成的 fileID
exampleRepository.save(exampleData);
return true;
}
private String saveFileToGridFS(MultipartFile file, String fileName) throws IOException {
// 将 MultipartFile 转换为 InputStream
try (InputStream inputStream = file.getInputStream()) {
// 保存文件到 GridFS
Object fileObjectId = gridFsTemplate.store(inputStream, fileName);
return fileObjectId.toString();
}
}
// ------------- 获取 ------------
@Override
public StreamingResponseBody getFile(String fileID) {
GridFSFile gridFSFile = gridFsTemplate.findOne(new Query(Criteria.where("_id").is(fileID)));
if (gridFSFile == null) {
throw new ViewException(ViewExceptionType.DATA_NOT_EXISTS);
}
return outputStream -> {
try (InputStream inputStream = gridFsTemplate.getResource(gridFSFile).getInputStream()) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
// 处理异常,例如记录日志或者抛出运行时异常
log.error("Error occurred while transferring file content", e);
throw new RuntimeException("Error occurred while transferring file content", e);
}
};
}
// ------------ 删除 ---------------------
@Override
public Boolean deleteById(String id) {
TbExample tbexample = exampleRepository.findById(id).orElse(null);
if (tbexample != null) {
// 首先,删除数据库记录
exampleRepository.deleteById(id);
// 然后,尝试从GridFS中删除文件
try {
// 如果使用的是文件ID作为GridFS中的_id
// gridFsTemplate.delete(new Query(Criteria.where("_id").is(tbexample.getFileID())));
// 如果文件是通过文件名存储在GridFS中,您需要使用文件名来删除
gridFsTemplate.delete(new Query(Criteria.where("filename").is(tbexample.getId() + ".json")));
return true;
} catch (Exception e) {
log.error("Failed to delete file from GridFS: " + tbexample.getFileID(), e);
throw new ViewException(ViewExceptionType.SERVER_ERROR, "删除文件失败");
}
} else {
throw new ViewException(ViewExceptionType.DATA_NOT_EXISTS);
}
}
上传后检查存储桶: