Projects:Process File upload Reference/Specs
Contents |
Overview
This project adds a new Reference in Openbravo core that let us upload files in Process Definitions and receive them in the server to process them.
Usage
File upload fields can be added to Process Definitions by creating a new parameter using the Process File Upload Reference.
![]() | Process File Upload Reference is meant to be used only in Process Definitions. If you try to set this reference to a Standard Window field, it will show an error message. |
When a Process contain file upload widgets, form data will be sent using a multipart/form-data request. Server's ActionHandler is aware of that and will parse the request parameters properly. For each file field sent, server will expose a parameters in the parameters map provided in doExecute(parameters, content) function containing a map with the file content as a steam an additional information.
For example, for a Process File Upload field named 'myfile', a parameter will be exposed as a Map with the following info:
myfile: { content: <java.io.InputStream>, // the file content as a stream fileName: <String>, // the file name size: <long> // file size in bytes }
File size limitation
The maximum file size users are allowed to upload are limited by default to 10MB. This is set in the preference Maximum Process File Upload size (MB). This file size check is performed both on the client and in the server side.
Implementation
Client side
Our file upload form field isc.OBProcessFileUpload inherits from Smartclient's isc.FileItem that wraps the <input type="file"> element within a multipart/form-data form that will contain the uploaded file's info. Smartclient's DynamicForm refers to this form using the following call:
view.theForm.getFileItemForm();
This can be used to determine whether it is a normal Process call or it may contain a file to upload. In the latter case we need to set the Process request type to multipart/form-data in order to be able to embed the uploaded file and the original file name. This logic is handled by ob-parameter-window-view.js in the actionHandlerCall function:
const hasFileItems = view.theForm.getFileItemForm(); if (hasFileItems) { // Create a new FormData object that contains the Process parameters and // the file info in those fields that are FileItems // Then make the multipart request using fetch fetch(actionHandler, { method: 'POST', body: formData}).then(...) } else { // Has no files to upload. Handle as a normal Process OB.RemoteCallManager.call(...); }
![]() | Note that fetch() is used to send the multipart request. This function is not supported by Internet Explorer 11. caniuse.com/fetch |
Server side
As mentioned before, in order to send files to the server, we should switch from a content-type=application/json request that does not support file upload to use a content-type=multipart/form-data request.
This implies that the request sent by the client changes from:
<server_url>/org.openbravo.client.kernel?processId=X&reportId=null&windowId=null&_action=Y
{ "_buttonValue": "DONE", "_params": { "text": "value" } }
To this in multiform/form-data:
<server_url>/org.openbravo.client.kernel?_action=Y
myfile: (binary) processId: X reportId: null windowId: null paramValues: { "_buttonValue":"DONE", "_params":{ "text":"value", "myfile":"C:\\fakepath\\file.png" } }
As you can see we can move processId, reportId and windowId to the multipart payload, and what was the JSON content of the request, it is now contained in the parameter paramValues. That change requires an extension to the server ActionHandler to support this new type of request.
To do that in the most flexible way, we refactored BaseActionHandler.java execute method to split its logic in individual methods that can be extended by children classes:
public void execute() { //This method first extract the parameters from the Request object Map<String, Object> parameters = this.extractParametersFromRequest(request); //Also extract the request content as an String String content = this.extractRequestContent(request, parameterMap); // Call the execute method passing the parsed parameters and content JSONObject result = this.execute(parameters, content); // Finally write the response object this.writeResponse(parameterMap, result, request, response); }
These individual calls are extended in BaseProcessActionHandler.java to change their implementation depending on whether the request is multipart or not. This can be determined using the following call:
boolean isMultipart = ServletFileUpload.isMultipartContent(request);