java - 如何将文件上传集成到 Spring Data REST 存储库中?
问题描述
我想将文件上传和下载集成到 Spring Data REST 存储库中。
我有一个DataFile
类(见下面的实现),它总是伴随着一些元数据。有一个DataFileRepository
存储有关元数据和文件之间关系的信息。
用户应该能够将文件与元数据一起上传到 web 应用程序表单中。元数据应该存储在数据库表中,同时文件被上传到某个文件存储,然后文件的路径再次存储在实体中。
我想使用 Spring Data REST 来做到这一点。我的方法是MultipartFile
在DataFile
实体中包含 a 并标记它@Transient
,@RestResource(exported = false)
因此它不在实体的任何表示中,但可以上传。上传时,在此步骤中将AbstractRepositoryEventListener
覆盖并上传文件。onBeforeCreate
但是通过这种方法,我正在努力将文件包含在 HTTP 请求正文中(的 JSON 表示DataFile
)。
其他方法将包括实现 a@RestRepositoryController
以增强DataFileRepository
但前面提到的方法是我最喜欢的。
使用 Spring Data REST(存储库)进行文件上传的最佳实践是什么?
@Data
@Entity
@Table(name = "data_files")
public class DataFile {
@Id
@GeneratedValue
@Setter(AccessLevel.NONE)
private long identifier;
@NotBlank
@Column(name = "path")
@RestResource(exported = false)
private String path;
@Embedded
private Metadata metadata;
public DataFile() {
metadata = new Metadata();
}
public DataFile(Metadata metadata) {
this.metadata = metadata;
}
}
解决方案
我没有找到只使用 Spring Data REST 的方法,但是这个解决方案很好地集成到了存储库中,并且不需要太多的手写代码。
以下是与问题之一不同的项目的此类解决方案的示例,但您应该明白这一点。
基本上创建一个@BasePathAwareController
,以便它适应您可能为 Spring Data REST 设置的基本路径。然后使用适当的路径在控制器内部创建映射。在这种情况下,有@PostMapping
一个到tutors
存储库的路径和一个在此处上传的相关附件的子路径。
您可以在此方法中进行验证,也可以在存储库的保存方法中引入一个方面。
在这种情况下,我使用了一个方面将处理应用于save()
存储库方法的每个调用。
@BasePathAwareController
@ExposesResourceFor(Tutor.class)
public class TutorRepositoryController {
private AttachmentAssembler attachmentAssembler;
private AttachmentRepository attachmentRepository;
private TutorRepository tutorRepository;
private RepositoryEntityLinks entityLinks;
@Autowired
public TutorRepositoryController(AttachmentAssembler attachmentAssembler,
AttachmentRepository attachmentRepository,
TutorRepository tutorRepository,
RepositoryEntityLinks entityLinks) {
this.attachmentAssembler = attachmentAssembler;
this.attachmentRepository = attachmentRepository;
this.tutorRepository = tutorRepository;
this.entityLinks = entityLinks;
}
@PostMapping(value = "/tutors/{id}/attachments/{name}")
public ResponseEntity<EntityModel<Attachment>> saveAttachment(@RequestParam("data") MultipartFile file,
@PathVariable long id,
@PathVariable String name) {
Tutor thisTutor = tutorRepository.findById(id);
File tempFile;
try {
tempFile = File.createTempFile(FilenameUtils.getBaseName(file.getOriginalFilename()),
FilenameUtils.getExtension(file.getOriginalFilename()));
StreamUtils.copy(file.getResource().getInputStream(), new FileOutputStream(tempFile));
} catch (IOException e) {
throw new RuntimeException("Could not create temporary file of uploaded file.");
}
Attachment saved =
attachmentRepository.save(new Attachment(name, thisTutor, tempFile, file.getOriginalFilename()));
return ResponseEntity.created(entityLinks.linkForItemResource(Attachment.class, saved.getIdentifier())
.toUri()).body(attachmentAssembler.toModel(saved));
}
}
这是处理文件上传的方面的代码。
@Aspect
@Component
public class TutorRepositoryAspect {
private Logger log = LoggerFactory.getLogger(TutorRepositoryAspect.class.getName());
private AttachmentRepository attachmentRepository;
private ApplicationConfiguration configuration;
private S3Service s3Service;
@Autowired
public TutorRepositoryAspect(AttachmentRepository attachmentRepository,
ApplicationConfiguration configuration,
S3Service s3Service) {
this.attachmentRepository = attachmentRepository;
this.configuration = configuration;
this.s3Service = s3Service;
}
@Pointcut(value = "execution(* com.example.tutor.backend.repository.attachment.AttachmentRepository.save(..)) && args(attachment)")
private void repositorySave(Attachment attachment) {
}
@Before(value = "repositorySave(attachment)", argNames = "attachment")
private Attachment beforeSave(Attachment attachment) {
log.info("Received request to add attachment to tutor \"{}\".", attachment.getIdentifier());
File file = attachment.getFile();
// Validation
String filePath = attachment.getAttachedTo().getIdentifier() + "-" + attachment.getName() + "-"
+ attachment.getOriginalFileName();
if (s3Service.isFilePresent(filePath)) {
log.error("Could not upload file as there is a file already with the same name.");
throw new IllegalArgumentException("File is already present.");
}
String s3Path = s3Service.uploadFile(file, filePath);
attachment.setFilePath(s3Path);
// This attachment is now being saved to database.
return attachment;
}
}
推荐阅读
- javascript - 如何并排设置 Visual Studio Code 和浏览器
- loops - Applescript - 回复已发送电子邮件中的最后一封电子邮件以获取 excel 中的地址列表
- css - 为什么我的样式表外部链接不起作用?文中总结
- android - 带有 Google 身份验证的 IdentityServer 4
- sql - 如何查找正在阻塞其他进程的进程
- java - Gson 返回未知类型的 Arraylist
- python - cvxpy如何制定取决于决策变量符号的表达式
- c - 删除函数二叉搜索树 BST,其父传递树指针作为参数
- azure - botbuilder的自定义命令
- visual-studio - 打开新文件时默认net48是构建目标,如何将netcoreapp3.1设为默认?