如何将文件上传集成到 Spring 数据 REST 存储库中?
How to integrate a file upload into Spring Data REST repositories?
我想将文件上传和下载集成到 Spring 数据 REST 存储库中。
我有一个 DataFile
class(见下面的实现),它总是伴随着一些元数据。有一个 DataFileRepository
来存储有关元数据和文件之间关系的信息。
用户应该能够上传网络应用程序形式的文件和元数据。元数据应存储在数据库表中,同时将文件上传到某个文件存储,然后将文件的路径再次存储在实体中。
我想使用 Spring Data REST 来做到这一点。我的方法是在 DataFile
实体中包含一个 MultipartFile
并将其标记为 @Transient
和 @RestResource(exported = false)
因此它不代表实体但可以上传。
上传后 AbstractRepositoryEventListener
会覆盖 onBeforeCreate
并在此步骤中上传文件。
但是通过这种方法,我很难将文件包含在 HTTP 请求正文中(DataFile
的 JSON 表示)。
其他方法包括实施 @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;
}
}
我想将文件上传和下载集成到 Spring 数据 REST 存储库中。
我有一个 DataFile
class(见下面的实现),它总是伴随着一些元数据。有一个 DataFileRepository
来存储有关元数据和文件之间关系的信息。
用户应该能够上传网络应用程序形式的文件和元数据。元数据应存储在数据库表中,同时将文件上传到某个文件存储,然后将文件的路径再次存储在实体中。
我想使用 Spring Data REST 来做到这一点。我的方法是在 DataFile
实体中包含一个 MultipartFile
并将其标记为 @Transient
和 @RestResource(exported = false)
因此它不代表实体但可以上传。
上传后 AbstractRepositoryEventListener
会覆盖 onBeforeCreate
并在此步骤中上传文件。
但是通过这种方法,我很难将文件包含在 HTTP 请求正文中(DataFile
的 JSON 表示)。
其他方法包括实施 @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;
}
}