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.
|