I have an entity in which besides regular fields there is a field of type FileDescriptor:
@NamePattern("The document in the following state: %s|cardState")
@Table(name = "DEMO_CARD_ITEM")
@Entity(name = "demo$CardItem")
public class CardItem extends StandardEntity {
private static final long serialVersionUID = -3956149009965224251L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="ID")
public UUID getId() {
return id;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "CARD_TYPE_ID", nullable = false)
protected CardType cardType;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "CARD_SUB_TYPE_ID", nullable = false)
protected CardSubType cardSubType;
@Column(name = "CARD_STATE", nullable = false)
protected String cardState =
ResourceBundle.getBundle("MessagesBundle", new Locale("ru","RU")).getString("cardState");
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "CARD_CREATION_DATE", nullable = false)
protected Date cardCreationDate = new Date();
@Column(name = "CARD_OUTCOME_NUMBER", nullable = false)
protected String cardOutcomeNumber;
@Column(name = "CARD_THEME", nullable = false)
protected String cardTheme;
@Column(name = "CARD_NUMBER", nullable = true)
protected String cardNumber;
@Column(name = "CARD_AUTO_CREATION", nullable = true)
protected Boolean cardAutoCreation;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "CARD_DATE", nullable = false)
protected Date cardDate;
@Column(name = "CARD_ORGANIZATION", nullable = false)
protected String cardOrganization;
@Column(name = "CARD_DELIVERY_METHOD", nullable = false)
protected String cardDeliveryMethod;
@Column(name = "CARD_ADDITIONAL_INFORMATION", nullable = true)
protected String cardAdditionalInformation;
@Column(name = "CARD_REGISTRATOR_NAME")
protected String cardRegistratorName;
@Column(name = "CARD_REGISTRATOR_NUMBER", nullable = false)
protected String cardRegistratorNumber;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "CARD_REGISTRATION_DATE", nullable = false)
protected Date cardRegistrationDate = new Date();
@Column(name = "CARD_REGISTRATOR_PERFORMERS", nullable = false)
protected String cardPerformers;
@Column(name = "CARD_FOR_REVIEW", nullable = true)
protected String cardForReview;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "IMAGE_FILE_ID")
protected FileDescriptor imageFile;
// SKIPPED
}
My card-item-edit.xml descriptor of the screen:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
caption="msg://editCaption"
class="com.client.web.item.CardItemEdit"
datasource="cardItemDs"
focusComponent="cardItemsGroup"
lookupComponent="cardItemsGroup"
messagesPack="com.client.web.item">
<dsContext>
<datasource id="cardItemDs" class="com.client.entity.CardItem" view="_local" allowCommit="false"/>
<collectionDatasource id="cardTypeDs" class="com.client.entity.CardType">
<query>
<![CDATA[select e from demo$CardType e]]>
</query>
</collectionDatasource>
<collectionDatasource id="cardSubTypeDs" class="com.client.entity.CardSubType">
<query>
<![CDATA[select s from demo$CardSubType s where s.cardType.id = :ds$cardTypeDs]]>
</query>
</collectionDatasource>
</dsContext>
<actions>
<action id="save" caption="mainMsg://actions.Ok" invoke="save" />
<action id="cancel" caption="mainMsg://actions.Cancel" invoke="cancel" />
</actions>
<dialogMode forceDialog="true" width="AUTO"/>
<layout spacing="true">
<hbox id="cardItemsGroup" spacing="true" margin="true">
<vbox spacing="true" margin="true">
<fieldGroup id="fieldGroup1" datasource="cardItemDs">
<column width="500px">
<field id="cardState" property="cardState" editable="false" datasource="cardItemDs" width="30%"/>
<field id="cardCreationDate" property="cardCreationDate" editable="false" datasource="cardItemDs" width="30%"/>
<field id="cardTypeField" caption="Тип документа" width="30%">
<lookupPickerField id="cardType" datasource="cardItemDs" property="cardType" optionsDatasource="cardTypeDs" width="30%"/>
</field>
<field id="cardSubTypeField" caption="Подтип документа" width="30%">
<lookupPickerField id="cardSubType" datasource="cardItemDs" property="cardSubType" optionsDatasource="cardSubTypeDs" width="30%"/>
</field>
<field id="cardTheme" datasource="cardItemDs" property="cardTheme" width="30%"/>
<field id="cardNumber" datasource="cardItemDs" property="cardNumber" width="30%"/>
<field id="cardAutoCreation" datasource="cardItemDs" property="cardAutoCreation" width="30%"/>
<field id="cardOutcomeNumber" datasource="cardItemDs" property="cardOutcomeNumber" width="30%"/>
<field id="cardDate" datasource="cardItemDs" property="cardDate" width="30%"/>
<field id="cardOrganization" datasource="cardItemDs" property="cardOrganization" width="30%"/>
<field id="cardDeliveryMethod" datasource="cardItemDs" property="cardDeliveryMethod" width="30%"/>
<field id="cardAdditionalInformation" datasource="cardItemDs" property="cardAdditionalInformation" width="30%"/>
</column>
</fieldGroup>
</vbox>
<vbox spacing="true" margin="true">
<fieldGroup id="fieldGroup2" datasource="cardItemDs">
<column width="500px">
<field id="cardRegistratorNumber" datasource="cardItemDs" property="cardRegistratorNumber" width="30%"/>
<field id="cardRegistrationDate" datasource="cardItemDs" property="cardRegistrationDate" width="30%"/>
<field id="cardPerformers" datasource="cardItemDs" property="cardPerformers" width="30%"/>
<field id="cardForReview" datasource="cardItemDs" property="cardForReview" width="30%"/>
<field id="cardRegistratorName" datasource="cardItemDs" property="cardRegistratorName" width="30%"/>
</column>
</fieldGroup>
</vbox>
</hbox>
<groupBox caption="Документ (изображение)" spacing="true" height="250px" width="250px" expand="embeddedImage">
<embedded id="embeddedImage" width="100%" align="MIDDLE_CENTER"/>
<hbox align="BOTTOM_LEFT" spacing="true">
<upload id="uploadField"/>
<button id="clearImageBtn" caption="Очистить" invoke="onClearImageBtnClick"/>
</hbox>
</groupBox>
<hbox id="actionsPane" spacing="true" visible="true">
<button id="saveBtn" action="save"/>
<button id="cancelBtn" action="cancel"/>
</hbox>
</layout>
</window>
My CardItemEdit controller of the screen:
public class CardItemEdit extends AbstractEditor<CardItem> {
@Inject
private Datasource<CardItem> cardItemDs;
@Inject
private LookupField cardType;
@Inject
private LookupField cardSubType;
@Named("fieldGroup1.cardState")
private Field cardState;
@Named("fieldGroup1.cardCreationDate")
private Field cardCreationDate;
@Named("fieldGroup1.cardTheme")
private Field cardTheme;
@Named("fieldGroup1.cardNumber")
private Field cardNumber;
@Named("fieldGroup1.cardAutoCreation")
private Field cardAutoCreation;
@Named("fieldGroup1.cardOutcomeNumber")
private Field cardOutcomeNumber;
@Named("fieldGroup1.cardDate")
private Field cardDate;
@Named("fieldGroup1.cardOrganization")
private Field cardOrganization;
@Named("fieldGroup1.cardDeliveryMethod")
private Field cardDeliveryMethod;
@Named("fieldGroup1.cardAdditionalInformation")
private Field cardAdditionalInformation;
@Named("fieldGroup2.cardRegistratorNumber")
private Field cardRegistratorNumber;
@Named("fieldGroup2.cardRegistrationDate")
private Field cardRegistrationDate;
@Named("fieldGroup2.cardPerformers")
private Field cardPerformers;
@Named("fieldGroup2.cardForReview")
private Field cardForReview;
@Named("fieldGroup2.cardRegistratorName")
private Field cardRegistratorName;
@Inject
private CardItemService cardItemService;
@Inject
private DataSupplier dataSupplier;
@Inject
private FileStorageService fileStorageService;
@Inject
private FileUploadingAPI fileUploadingAPI;
@Inject
private ExportDisplay exportDisplay;
@Inject
private Embedded embeddedImage;
@Inject
private FileUploadField uploadField;
@Inject
private Button downloadImageBtn;
@Inject
private Button clearImageBtn;
private static final int IMG_HEIGHT = 190;
private static final int IMG_WIDTH = 220;
@Override
public void init(Map<String, Object> params) {
uploadField.addFileUploadSucceedListener(event -> {
FileDescriptor fd = uploadField.getFileDescriptor();
try {
fileUploadingAPI.putFileIntoStorage(uploadField.getFileId(), fd);
} catch (FileStorageException e) {
throw new RuntimeException("Error saving file to FileStorage", e);
}
getItem().setImageFile(dataSupplier.commit(fd));
displayImage();
});
uploadField.addFileUploadErrorListener(event ->
showNotification("File upload error", NotificationType.HUMANIZED));
cardItemDs.addItemPropertyChangeListener(event -> {
// if ("imageFile".equals(event.getProperty()))
// updateImageButtons(event.getValue() != null);
});
}
@Override
protected void postInit() {
displayImage();
// updateImageButtons(getItem().getImageFile() != null);
}
public void onDownloadImageBtnClick() {
if (getItem().getImageFile() != null)
exportDisplay.show(getItem().getImageFile(), ExportFormat.OCTET_STREAM);
}
public void onClearImageBtnClick() {
getItem().setImageFile(null);
displayImage();
}
// private void updateImageButtons(boolean enable) {
// downloadImageBtn.setEnabled(enable);
// clearImageBtn.setEnabled(enable);
// }
private void displayImage() {
byte[] bytes = null;
if (getItem().getImageFile() != null) {
try {
bytes = fileStorageService.loadFile(getItem().getImageFile());
} catch (FileStorageException e) {
showNotification("Unable to load image file", NotificationType.HUMANIZED);
}
}
if (bytes != null) {
embeddedImage.setSource(getItem().getImageFile().getName(), new ByteArrayInputStream(bytes));
embeddedImage.setType(Embedded.Type.IMAGE);
BufferedImage image;
try {
image = ImageIO.read(new ByteArrayInputStream(bytes));
int width = image.getWidth();
int height = image.getHeight();
if (((double) height / (double) width) > ((double) IMG_HEIGHT / (double) IMG_WIDTH)) {
embeddedImage.setHeight(String.valueOf(IMG_HEIGHT));
embeddedImage.setWidth(String.valueOf(width * IMG_HEIGHT / height));
} else {
embeddedImage.setWidth(String.valueOf(IMG_WIDTH));
embeddedImage.setHeight(String.valueOf(height * IMG_WIDTH / width));
}
} catch (IOException e) {
}
// refresh image
embeddedImage.setVisible(false);
embeddedImage.setVisible(true);
} else {
embeddedImage.setVisible(false);
}
}
public void save() {
CardItem cardItem = new CardItem();
cardItem.setImageFile(uploadField.getFileDescriptor());
cardItem.setCardType(cardType.getValue());
cardItem.setCardSubType(cardSubType.getValue());
cardItem.setCardOutcomeNumber(cardOutcomeNumber.getValue());
cardItem.setCardOrganization(cardOrganization.getValue());
cardItem.setCardDeliveryMethod(cardDeliveryMethod.getValue());
cardItem.setCardDate(cardDate.getValue());
cardItem.setCardCreationDate(cardCreationDate.getValue());
cardItem.setCardAdditionalInformation(cardAdditionalInformation.getValue());
cardItem.setCardRegistratorName(cardRegistratorName.getValue());
cardItem.setCardTheme(cardTheme.getValue());
cardItem.setCardNumber(cardNumber.getValue());
cardItem.setCardAutoCreation(cardAutoCreation.getValue());
cardItem.setCardRegistratorNumber(cardRegistratorNumber.getValue());
cardItem.setCardRegistrationDate(cardRegistrationDate.getValue());
cardItem.setCardPerformers(cardPerformers.getValue());
cardItem.setCardForReview(cardForReview.getValue());
cardItem.setCardRegistratorName(cardRegistratorName.getValue());
cardItemService.saveCardItem(cardItem);
close(COMMIT_ACTION_ID);
}
public void cancel() {
close(CLOSE_ACTION_ID);
}
}
My CardItemService:
@Service(CardItemService.NAME)
public class CardItemServiceBean implements CardItemService {
@Inject
private Persistence persistence;
@Override
public UUID saveCardItem(CardItem cardItem) {
try(Transaction tx = persistence.createTransaction()) {
persistence.getEntityManager().persist(cardItem);
tx.commit();
}
return cardItem.getId();
}
}
My views.xml:
<views xmlns="http://schemas.haulmont.com/cuba/view.xsd">
<view class="com.client.entity.CardItem" name="card-item-browse" extends="_local">
<property name="cardType"/>
<property name="cardSubType"/>
</view>
<view class="com.client.entity.CardItem" name="card-item-edit">
<property name="imageFile" view="_local">
</property>
</view>
</views>
So, when I create a new document, I can attach, for example, an image:
I think it’s suspicious in the save method:
cardItem.setImageFile(uploadField.getFileDescriptor());
Do I save the files correctly?
How is the uploaded file stored? In the server file system or in the database?
I would be very grateful for the information. Thanks to all.