/
AvatarField.java
159 lines (129 loc) · 4.76 KB
/
AvatarField.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package org.vaadin.examples.form.ui.components;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import org.vaadin.examples.form.data.AvatarImage;
import com.vaadin.flow.component.customfield.CustomField;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.upload.SucceededEvent;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.server.StreamResource;
/**
* A custom Vaadin component that allows users to upload an avatar image.
* <p>
* Can be used with the {@link Binder}. Note the type below; this Component can
* only modify {@link AvatarImage} data.
*/
public class AvatarField extends CustomField<AvatarImage> {
/**
* We store the value here.
*/
private AvatarImage value;
/**
* This is where any upload content will be written to
*/
private ByteArrayOutputStream outputStream;
private Image currentAvatar;
private Upload upload;
public AvatarField(String caption) {
this();
setLabel(caption);
}
public AvatarField() {
// <img> that shows the current avatar
currentAvatar = new Image();
currentAvatar.setAlt("avatar image");
currentAvatar.setMaxHeight("100px");
currentAvatar.getStyle().set("margin-right", "15px");
currentAvatar.setVisible(false); // see updateImage()
// create the upload component and delegate actions to the receiveUpload method
upload = new Upload(this::receiveUpload);
upload.getStyle().set("flex-grow", "1");
// listen to state changes
upload.addSucceededListener(e -> uploadSuccess(e));
upload.addFailedListener(e -> setFailed(e.getReason().getMessage()));
upload.addFileRejectedListener(e -> setFailed(e.getErrorMessage()));
// only allow images to be uploaded
upload.setAcceptedFileTypes("image/*");
// only allow single file at a time
upload.setMaxFiles(1);
// set max file size to 1 MB
upload.setMaxFileSize(1 * 1024 * 1024);
// component layouting
Div wrapper = new Div();
wrapper.add(currentAvatar, upload);
wrapper.getStyle().set("display", "flex");
add(wrapper);
}
/*
* We need to implement this method so that this class works with the Binder.
* This method should return the current value.
*/
@Override
protected AvatarImage generateModelValue() {
return value;
}
/*
* We need to implement this method so that this class works with the Binder.
* This method should store the given value and update the visuals to the new
* value.
*/
@Override
protected void setPresentationValue(AvatarImage newPresentationValue) {
value = newPresentationValue;
updateImage();
}
/**
* Called when a user initializes an upload.
* <p>
* We prepare the bean and a destination for the binary data; Vaadin will take
* care of the actual network operations.
*/
private OutputStream receiveUpload(String fileName, String mimeType) {
// clear old errors for better user experience
setInvalid(false);
// create new value bean to store the data
value = new AvatarImage();
value.setName(fileName);
value.setMime(mimeType);
// set up receiving Stream
outputStream = new ByteArrayOutputStream();
return outputStream;
}
/**
* Called when an upload is successfully completed.
*/
private void uploadSuccess(SucceededEvent e) {
// store the binary data into our bean
value.setImage(outputStream.toByteArray());
// fire value changes so that Binder can do its thing
setModelValue(value, true);
// show the new image
updateImage();
// clear the upload component 'finished files' list for a cleaner appearance.
// there is yet no API for it on the server side, see
// https://github.com/vaadin/vaadin-upload-flow/issues/96
upload.getElement().executeJs("this.files=[]");
}
/**
* Shows an error message to the user.
*/
private void setFailed(String message) {
setInvalid(true);
setErrorMessage(message);
}
/**
* Updates avatar image content or hide if empty
*/
private void updateImage() {
if (value != null && value.getImage() != null) {
currentAvatar.setSrc(new StreamResource("avatar", () -> new ByteArrayInputStream(value.getImage())));
currentAvatar.setVisible(true);
} else {
currentAvatar.setSrc("");
currentAvatar.setVisible(false);
}
}
}