Skip to content

Commit 847a21c

Browse files
committed
error handling for exceeding max allowed file size (#957)
1 parent 1d1577b commit 847a21c

4 files changed

Lines changed: 105 additions & 30 deletions

File tree

imixs-workflow-faces/src/main/java/org/imixs/workflow/faces/fileupload/AjaxFileUploadServlet.java

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,41 @@ public class AjaxFileUploadServlet extends HttpServlet {
6666
@Override
6767
protected void doPost(HttpServletRequest httpRequest, HttpServletResponse response)
6868
throws ServletException, IOException {
69+
6970
if (isPostFileUploadRequest(httpRequest)) {
70-
logger.fine("......add files...");
71+
logger.info("......file upload request received...");
7172
List<FileData> fileDataList = getFilesFromRequest(httpRequest);
72-
// now update the workitem....
73+
74+
// null signals a FileTooLargeException from Undertow
75+
if (fileDataList == null) {
76+
// Read maxFileSize from context-param or fall back to @MultipartConfig
77+
String configValue = getServletContext().getInitParameter("imixs.fileupload.maxFileSize");
78+
MultipartConfig annotation = this.getClass().getAnnotation(MultipartConfig.class);
79+
long maxFileSize = (configValue != null)
80+
? Long.parseLong(configValue)
81+
: annotation.maxFileSize();
82+
83+
logger.warning("......upload aborted - file too large, maxFileSize=" + maxFileSize);
84+
response.setStatus(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);
85+
response.setContentType("application/json;charset=UTF-8");
86+
PrintWriter out = response.getWriter();
87+
out.write("{ \"error\": \"FILE_TOO_LARGE\", \"maxFileSize\": " + maxFileSize + " }");
88+
out.close();
89+
return;
90+
}
91+
92+
logger.info("......processing " + fileDataList.size() + " file(s)...");
7393
if (fileUploadController != null) {
74-
// check workitem... issue
7594
if (fileUploadController.getWorkitem() != null) {
76-
logger.fine("......prüfe file data Liste...");
7795
for (FileData filedata : fileDataList) {
78-
logger.fine("......add new fileData object..." + filedata.getName());
96+
logger.info("......adding fileData: " + filedata.getName());
7997
fileUploadController.addFileUpload(filedata);
8098
}
99+
} else {
100+
logger.warning("......workitem is null - files cannot be stored");
81101
}
102+
} else {
103+
logger.warning("......fileUploadController is null");
82104
}
83105
writeJsonMetadata(response, httpRequest.getRequestURI());
84106
}
@@ -186,50 +208,35 @@ private String getFilename(Part part) {
186208
*/
187209
private List<FileData> getFilesFromRequest(HttpServletRequest httpRequest) {
188210
logger.finest("......Looping parts");
189-
190211
List<FileData> fileDataList = new ArrayList<FileData>();
191212
try {
192213
for (Part p : httpRequest.getParts()) {
193214
byte[] b = new byte[(int) p.getSize()];
194215
p.getInputStream().read(b);
195216
p.getInputStream().close();
196-
// params.put(p.getName(), new String[] { new String(b) });
197-
198-
// test if part contains a file
199217
String fileName = getFilename(p);
200218
if (fileName != null) {
201-
202-
/*
203-
* issue #106
204-
*
205-
* https://developer.jboss.org/message/941661#941661
206-
*
207-
* Here we test of the encoding and try to convert to utf-8.
208-
*/
209219
byte fileNameISOBytes[] = fileName.getBytes("iso-8859-1");
210220
String fileNameUTF8 = new String(fileNameISOBytes, "UTF-8");
211221
if (fileName.length() != fileNameUTF8.length()) {
212-
// convert to utf-8
213222
logger.finest("......filename seems to be ISO-8859-1 encoded");
214223
fileName = new String(fileName.getBytes("iso-8859-1"), "utf-8");
215224
}
216-
217-
// extract the file content...
218-
FileData fileData = null;
219-
logger.log(Level.FINEST, "......filename : {0}, contentType {1}",
220-
new Object[] { fileName, p.getContentType() });
221-
fileData = new FileData(fileName, b, p.getContentType(), null);
225+
FileData fileData = new FileData(fileName, b, p.getContentType(), null);
222226
fileDataList.add(fileData);
223-
227+
logger.info("......file added: " + fileName + " size: " + b.length + " bytes");
224228
}
225229
}
226-
230+
} catch (IllegalStateException ex) {
231+
// Undertow wraps FileTooLargeException in an IllegalStateException
232+
logger.warning("......file upload rejected - file too large: " + ex.getMessage());
233+
// Signal the error to the caller by returning null
234+
return null;
227235
} catch (IOException ex) {
228236
logger.log(Level.SEVERE, null, ex);
229237
} catch (ServletException ex) {
230238
logger.log(Level.SEVERE, null, ex);
231239
}
232-
233240
return fileDataList;
234241
}
235242

imixs-workflow-faces/src/main/resources/META-INF/resources/imixs/css/imixs.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,13 @@
8383
}
8484
.imixsfileupload .remove-link:hover {
8585
text-decoration: none;
86+
}
87+
88+
.imixsfileupload-error {
89+
color: #c0392b;
90+
font-weight: bold;
91+
padding: 8px;
92+
border: 1px solid #e74c3c;
93+
border-radius: 4px;
94+
background-color: #fdecea;
8695
}

imixs-workflow-faces/src/main/resources/META-INF/resources/imixs/imixs-faces.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,18 +371,34 @@ imixsFileUploadRefresh = function () {
371371
// no headers needed
372372
})
373373
.then(response => {
374+
if (response.status === 413) {
375+
// Clear the file input to prevent re-sending on next form submit
376+
const dataTransfer = new DataTransfer();
377+
fileInput.files = dataTransfer.files;
378+
currentFileUploadsMap.set(container, []);
379+
return response.json().then(data => {
380+
const limitText = data.maxFileSize
381+
? fileSizeToString(data.maxFileSize)
382+
: 'the maximum allowed size';
383+
fileTableContainer.innerHTML =
384+
'<p class="imixsfileupload-error">Upload failed: File exceeds ' + limitText + '.</p>';
385+
throw new Error('FileTooLarge');
386+
});
387+
}
374388
if (!response.ok) {
375-
throw new Error('Upload failed');
389+
fileTableContainer.innerHTML =
390+
'<p class="imixsfileupload-error">Upload failed: Server error (' + response.status + ').</p>';
391+
throw new Error('Upload failed with status: ' + response.status);
376392
}
377-
378-
return response.json();
393+
return response.json();
379394
})
380395
.then(data => {
381396
if (refreshFileUploadAjaxTable) {
382397
refreshFileUploadAjaxTable();
383398
}
384399
})
385400
.catch(error => {
401+
// Log to console - UI message is already set above
386402
console.error('Upload error: ', error);
387403
});
388404
} else {

src/site/markdown/webtools/fileupload.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,46 @@ Optional you can also use a fileUpload widget with Ajax support. This allows the
6969
workitem="#{documentController.document}"
7070
context_url="#{facesContext.externalContext.requestContextPath}/api/" />
7171
```
72+
73+
## File Size Configuration
74+
75+
The Ajax file upload widget uses the `AjaxFileUploadServlet` to handle file uploads.
76+
By default, the maximum file size is limited to **10 MB** per file and **50 MB** per request.
77+
78+
A developer can override these defaults by adding the following configuration to the `web.xml`:
79+
80+
```xml
81+
<!-- Override the default file size limits for the AjaxFileUploadServlet -->
82+
<servlet>
83+
<servlet-name>AjaxFileUploadServlet</servlet-name>
84+
<servlet-class>org.imixs.workflow.faces.fileupload.AjaxFileUploadServlet</servlet-class>
85+
<multipart-config>
86+
<max-file-size>20971520</max-file-size> <!-- 20 MB -->
87+
<max-request-size>52428800</max-request-size> <!-- 50 MB -->
88+
<file-size-threshold>1048576</file-size-threshold>
89+
</multipart-config>
90+
</servlet>
91+
<servlet-mapping>
92+
<servlet-name>AjaxFileUploadServlet</servlet-name>
93+
<url-pattern>/fileupload/*</url-pattern>
94+
</servlet-mapping>
95+
96+
<!-- Provide the max file size for user-friendly error messages -->
97+
<context-param>
98+
<param-name>imixs.fileupload.maxFileSize</param-name>
99+
<param-value>20971520</param-value>
100+
</context-param>
101+
```
102+
103+
**Note:** The `context-param` `imixs.fileupload.maxFileSize` must match the `max-file-size`
104+
value in the `multipart-config`. It is used to display a meaningful error message to the
105+
user when an upload exceeds the allowed file size.
106+
107+
The two size parameters have different meanings:
108+
109+
- `max-file-size` - defines the maximum size of a **single file** within one upload request
110+
- `max-request-size` - defines the maximum size of the **entire HTTP request**, i.e. the sum
111+
of all files uploaded at once plus form fields
112+
113+
Example: With a `max-file-size` of 20 MB and a `max-request-size` of 50 MB, a user could
114+
upload two files of 20 MB each simultaneously, but not three files of 20 MB each.

0 commit comments

Comments
 (0)