Uploading and saving files
The FileReference entity
FileManagerModule provides a FileReference
entity to handle file upload and the actual saving of a physical file. When saving a file the MultipartFileToFileReferenceConverter
is used
to convert the file into a FileReference
. When not overridden the MultipartFileToFileReferenceConverter
uses the default
FileRepository
to persist the files trough the FileReferenceService
. If you want to customize the FileRepository
that is used
you can read the section about changing the default FileRegistry for saving a FileReference.
When saving a FileReference
the physical file always get’s persisted in temp storage by default despite of validation errors.
This is so the user doesn’t have to re-upload the file if there are other validation errors.
Single file upload
An example for saving a file and linking it to an Entity.
@Entity
@Table(name = "test_invoice")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
public class Invoice extends SettableIdBasedEntity<Invoice> {
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "seq_test_invoice_id")
@GenericGenerator(
name = "seq_test_invoice_id",
strategy = AcrossSequenceGenerator.STRATEGY,
parameters = {
@org.hibernate.annotations.Parameter(name = "sequenceName", value = "seq_test_invoice_id"),
@org.hibernate.annotations.Parameter(name = "allocationSize", value = "1")
}
)
private Long id;
@NotBlank
@Length(max = 200)
@Column(name = "name")
private String name;
@NotNull
@ManyToOne
@JoinColumn(name = "attachment_id", referencedColumnName = "id")
private FileReference attachment;
}
Uploading multiple files
The FileReference
entity can be used to handle multi-file-uploads as well.
@Entity
@Table(name = "test_expense")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
public class Expense extends SettableIdBasedEntity<Expense> {
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "seq_test_expense_id")
@GenericGenerator(
name = "seq_test_expense_id",
strategy = AcrossSequenceGenerator.STRATEGY,
parameters = {
@org.hibernate.annotations.Parameter(name = "sequenceName", value = "seq_test_expense_id"),
@org.hibernate.annotations.Parameter(name = "allocationSize", value = "1")
}
)
private Long id;
@NotBlank
@Length(max = 200)
@Column(name = "name")
private String name;
@NotNull
@NotEmpty
@ManyToMany
@Setter(AccessLevel.NONE)
@JoinTable(
name = "expense_attachments",
joinColumns = @JoinColumn(name = "fr_expense_expense_id"),
inverseJoinColumns = @JoinColumn(name = "fr_expense_fr_id")
)
private List<FileReference> attachments;
public void setAttachments( List<FileReference> attachments ) {
this.attachments = attachments.stream().filter( Objects::nonNull ).collect( Collectors.toList() );
}
}
By default when saving a FileReference
the default FileRepository
is being used.
The example below shows how the physical file can be moved to a different FileRepository
when there are
no validation errors and the entity is saved. This can configured by overriding the saveConsumer of your
entity.
@Configuration
@RequiredArgsConstructor
public class ExpenseEntityConfiguration implements EntityConfigurer {
private final FileReferenceService fileReferenceService;
@Override
public void configure(EntitiesConfigurationBuilder entities) {
entities.withType(Expense.class)
.properties(
props -> props
.property("attachments[]")
.controller(ctl -> ctl
.withBindingContext(FileReference.class)
.saveConsumer(
(ctx, fr) -> fileReferenceService
.changeFileRepository(fr.getNewValue(), "permanent", true)
)
)
);
}
}
Change the default FileRepository used for persisting a FileReference
You can override the FileRepository
that is used to persist the physical files that are represented by
a FileReference
.
@Configuration
public class DefaultFileReferenceFileRepository {
@Autowired
public void changeDefaultFileRepositoryForFileReferences(MultipartFileToFileReferenceConverter multipartFileToFileReferenceConverter) {
multipartFileToFileReferenceConverter.setRepositoryId("demo");
}
}
Manually registering a FileReference property
You can manually register a FileReference
property on an entity if you don’t want to use
the default way of saving the FileReference
with hibernate.
@Entity
@Table(name = "test_person")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
public class Person extends SettableIdBasedEntity<Person> {
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "seq_test_person_id")
@GenericGenerator(
name = "seq_test_person_id",
strategy = AcrossSequenceGenerator.STRATEGY,
parameters = {
@org.hibernate.annotations.Parameter(name = "sequenceName", value = "seq_test_person_id"),
@org.hibernate.annotations.Parameter(name = "allocationSize", value = "1")
}
)
private Long id;
@NotBlank
@Length(max = 200)
@Column(name = "name")
private String name;
@Column(name = "photo_id")
private Long photoId;
}
There is only a photoId defined to hold the fileReferenceId. We use an EntityConfigurer
to hide that property
and create a custom photo property with FileReference
as propertyType.
@Configuration
@RequiredArgsConstructor
public class PersonEntityConfiguration implements EntityConfigurer {
private final FileReferenceRepository fileReferenceRepository;
@Override
public void configure(EntitiesConfigurationBuilder entities) {
entities.withType(Person.class)
.properties(
props -> props
.property("photoId").and()
.property("photo")
.propertyType(FileReference.class)
.displayName("Photo")
.readable(true)
.writable(true)
.hidden(false)
.controller( c -> c.withTarget( Person.class, FileReference.class )
.valueFetcher( person -> person.getPhotoId() != null ? fileReferenceRepository
.findOne( person.getPhotoId() ) : null )
.applyValueConsumer(
( person, fileReference ) -> {
if ( fileReference.getNewValue() != null && !fileReference.isDeleted() ) {
person.setPhotoId( fileReference.getNewValue().getId() );
}
else {
person.setPhotoId( null );
}
} )
)
.attribute(EntityAttributes.FORM_ENCTYPE, FormViewElement.ENCTYPE_MULTIPART)
);
}
}
For this example we hide the photoId property in favor of our custom created photo property.
Our custom photo property has a FileReference
as propertyType and will implement the controller method
to handle the correct saving and showing of our photo file.
The valueFetcher in the controller method is used to transform the photoId into a FileReference
.
The applyValueConsumer
on the other hand is used to set the photoId on save.
By default when using a FileReference the right FORM_ENCTYPE is set.
If you are using FileReference as a custom property you have to set the FORM_ENCTYPE
to ENCTYPE_MULTIPART yourself.
|