1+ /*
2+ * Imixs-Workflow
3+ *
4+ * Copyright (C) 2001-2020 Imixs Software Solutions GmbH,
5+ * http://www.imixs.com
6+ *
7+ * This program is free software; you can redistribute it and/or
8+ * modify it under the terms of the GNU General Public License
9+ * as published by the Free Software Foundation; either version 2
10+ * of the License, or (at your option) any later version.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+ * General Public License for more details.
16+ *
17+ * You can receive a copy of the GNU General Public
18+ * License at http://www.gnu.org/licenses/gpl.html
19+ *
20+ * Project:
21+ * https://www.imixs.org
22+ * https://github.com/imixs/imixs-workflow
23+ *
24+ * Contributors:
25+ * Imixs Software Solutions GmbH - Project Management
26+ * Ralph Soika - Software Developer
27+ */
28+
29+ package org .imixs .workflow .faces .fileupload ;
30+
31+ import java .io .IOException ;
32+ import java .io .PrintWriter ;
33+ import java .net .URLDecoder ;
34+ import java .util .ArrayList ;
35+ import java .util .List ;
36+ import java .util .logging .Level ;
37+ import java .util .logging .Logger ;
38+
39+ import org .imixs .workflow .FileData ;
40+
41+ import jakarta .inject .Inject ;
42+ import jakarta .servlet .ServletException ;
43+ import jakarta .servlet .ServletOutputStream ;
44+ import jakarta .servlet .ServletResponse ;
45+ import jakarta .servlet .annotation .MultipartConfig ;
46+ import jakarta .servlet .annotation .WebServlet ;
47+ import jakarta .servlet .http .HttpServlet ;
48+ import jakarta .servlet .http .HttpServletRequest ;
49+ import jakarta .servlet .http .HttpServletResponse ;
50+ import jakarta .servlet .http .Part ;
51+
52+ /**
53+ * The AjaxFileUploadServlet is a Multipart-Servlet 3.0. It is used by the
54+ * imixsFileUploadAjax widget. The widget handles the upload of multiple files
55+ * and supports drag & drop functionality. The servlet is configured with a max
56+ * file size to 10MB, and a max request size of 50MB.
57+ *
58+ * @author rsoika
59+ */
60+ @ WebServlet (urlPatterns = { "/fileupload/*" })
61+ @ MultipartConfig (fileSizeThreshold = 1024 * 1024 , maxFileSize = 1024 * 1024 * 10 , maxRequestSize = 1024 * 1024 * 50 )
62+ public class AjaxFileUploadServlet extends HttpServlet {
63+
64+ private static final long serialVersionUID = 1L ;
65+ private static final String REQUEST_METHOD_POST = "POST" ;
66+ private static final String REQUEST_METHOD_GET = "GET" ;
67+ private static final String CONTENT_TYPE_MULTIPART = "multipart/" ;
68+ private static final String CONTENT_DISPOSITION = "content-disposition" ;
69+ private static final String CONTENT_DISPOSITION_FILENAME = "filename" ;
70+
71+ private static final Logger logger = Logger .getLogger (AjaxFileUploadServlet .class .getName ());
72+
73+ @ Inject
74+ FileUploadController fileUploadController ;
75+
76+ /**
77+ * Upload files to stored in the current user session
78+ */
79+ @ Override
80+ protected void doPost (HttpServletRequest httpRequest , HttpServletResponse response )
81+ throws ServletException , IOException {
82+ if (isPostFileUploadRequest (httpRequest )) {
83+ logger .fine ("......add files..." );
84+ List <FileData > fileDataList = getFilesFromRequest (httpRequest );
85+ // now update the workitem....
86+ if (fileUploadController != null ) {
87+ // check workitem... issue
88+ if (fileUploadController .getWorkitem () != null ) {
89+ logger .fine ("......prüfe file data Liste..." );
90+ for (FileData filedata : fileDataList ) {
91+ logger .fine ("......add new fileData object..." + filedata .getName ());
92+ fileUploadController .addFileUpload (filedata );
93+ }
94+ }
95+ }
96+ writeJsonMetadata (response , httpRequest .getRequestURI ());
97+ }
98+ }
99+
100+ /**
101+ * Getter method to return the file content from the fileData list stored in the
102+ * current user
103+ */
104+ @ Override
105+ protected void doGet (HttpServletRequest httpRequest , HttpServletResponse httpResponse )
106+ throws ServletException , IOException {
107+
108+ // check cancel upload...
109+ if (isGetFileUploadRequest (httpRequest )) {
110+ int iCancel = httpRequest .getRequestURI ().indexOf ("/fileupload/file/" );
111+ String filename = httpRequest .getRequestURI ().substring (iCancel + 17 );
112+ // urldecoding...
113+ filename = URLDecoder .decode (filename , "UTF-8" );
114+ if (fileUploadController != null ) {
115+ // check workitem... issue
116+ if (fileUploadController .getWorkitem () != null ) {
117+ FileData fileData = fileUploadController .getFileUpload (filename );
118+ // write contenremoveFile(filename);
119+ if (fileData != null ) {
120+ writeFileContent (httpResponse , fileData );
121+ } else {
122+ httpResponse .sendError (HttpServletResponse .SC_NOT_FOUND );
123+ }
124+ }
125+ }
126+ }
127+ }
128+
129+ /**
130+ * checks if the httpRequest is a fileupload get request...
131+ *
132+ * @param httpRequest
133+ * @return
134+ */
135+ private boolean isGetFileUploadRequest (HttpServletRequest httpRequest ) {
136+ String uri = httpRequest .getRequestURI ();
137+
138+ return (REQUEST_METHOD_GET .equalsIgnoreCase (httpRequest .getMethod ())
139+ && !(uri .endsWith ("/fileupload" ) || uri .endsWith ("/fileupload/" )));
140+
141+ }
142+
143+ /**
144+ * Returns a file attachment located in the property $file of the specified
145+ * workitem
146+ *
147+ * The file name will be encoded. With a URLDecode the filename is decoded in
148+ * different formats and searched in the file list. This is not a nice solution.
149+ *
150+ * @param uniqueid
151+ * @return
152+ * @throws IOException
153+ */
154+ private void writeFileContent (ServletResponse response , FileData fileData ) throws IOException {
155+ logger .finest ("......write file content..." );
156+ ServletOutputStream output = response .getOutputStream ();
157+ output .write (fileData .getContent ());
158+ // now return json string of uploaded files....
159+ response .setContentType (fileData .getContentType ());
160+ output .close ();
161+ }
162+
163+ /**
164+ * checks if the httpRequest is a fileupload
165+ *
166+ * @param httpRequest
167+ * @return
168+ */
169+ private boolean isPostFileUploadRequest (HttpServletRequest httpRequest ) {
170+ String sContentType = httpRequest .getContentType ();
171+ logger .log (Level .FINE , "......contentType={0}" , sContentType );
172+
173+ return (REQUEST_METHOD_POST .equalsIgnoreCase (httpRequest .getMethod ()) && httpRequest .getContentType () != null
174+ && sContentType .toLowerCase ().startsWith (CONTENT_TYPE_MULTIPART ));
175+ }
176+
177+ /**
178+ * test and extracts the filename of a http request part. The method returns
179+ * null if the part dose not contain a file
180+ *
181+ * @param part
182+ * @return - filename or null if not a file content
183+ */
184+ private String getFilename (Part part ) {
185+ for (String cd : part .getHeader (CONTENT_DISPOSITION ).split (";" )) {
186+ if (cd .trim ().startsWith (CONTENT_DISPOSITION_FILENAME )) {
187+ return cd .substring (cd .indexOf ('=' ) + 1 ).trim ().replace ("\" " , "" );
188+ }
189+ }
190+ return null ;
191+ }
192+
193+ /**
194+ * This method converts mulitple files from the httpRequest into a list of
195+ * FileData objects.
196+ *
197+ * @param httpRequest
198+ * @return list of FileData objects
199+ */
200+ private List <FileData > getFilesFromRequest (HttpServletRequest httpRequest ) {
201+ logger .finest ("......Looping parts" );
202+
203+ List <FileData > fileDataList = new ArrayList <FileData >();
204+ try {
205+ for (Part p : httpRequest .getParts ()) {
206+ byte [] b = new byte [(int ) p .getSize ()];
207+ p .getInputStream ().read (b );
208+ p .getInputStream ().close ();
209+ // params.put(p.getName(), new String[] { new String(b) });
210+
211+ // test if part contains a file
212+ String fileName = getFilename (p );
213+ if (fileName != null ) {
214+
215+ /*
216+ * issue #106
217+ *
218+ * https://developer.jboss.org/message/941661#941661
219+ *
220+ * Here we test of the encoding and try to convert to utf-8.
221+ */
222+ byte fileNameISOBytes [] = fileName .getBytes ("iso-8859-1" );
223+ String fileNameUTF8 = new String (fileNameISOBytes , "UTF-8" );
224+ if (fileName .length () != fileNameUTF8 .length ()) {
225+ // convert to utf-8
226+ logger .finest ("......filename seems to be ISO-8859-1 encoded" );
227+ fileName = new String (fileName .getBytes ("iso-8859-1" ), "utf-8" );
228+ }
229+
230+ // extract the file content...
231+ FileData fileData = null ;
232+ logger .log (Level .FINEST , "......filename : {0}, contentType {1}" ,
233+ new Object [] { fileName , p .getContentType () });
234+ fileData = new FileData (fileName , b , p .getContentType (), null );
235+ fileDataList .add (fileData );
236+
237+ }
238+ }
239+
240+ } catch (IOException ex ) {
241+ logger .log (Level .SEVERE , null , ex );
242+ } catch (ServletException ex ) {
243+ logger .log (Level .SEVERE , null , ex );
244+ }
245+
246+ return fileDataList ;
247+ }
248+
249+ /**
250+ * This method write a JSON meta data structure for uploaded files into the
251+ * httpResponse. This structure is used by a ajax call to extract the result
252+ *
253+ * <code>
254+ {
255+ "files": [
256+ {
257+ "url": "0:0:0:0:0:0:0:1",
258+ "thumbnail_url": "",
259+ "name": "start.gif",
260+ "type": "image/gif",
261+ "size": 128,
262+ "delete_url": "",
263+ "delete_type": "DELETE"
264+ }
265+ ]
266+ }
267+ * </code>
268+ *
269+ *
270+ * * @see https://github.com/blueimp/jQuery-File-Upload/wiki/JSON-Response
271+ *
272+ * @throws IOException
273+ *
274+ */
275+ private void writeJsonMetadata (ServletResponse response , String context_url ) throws IOException {
276+
277+ response .setContentType ("application/json;charset=UTF-8" );
278+ PrintWriter out = response .getWriter ();
279+
280+ // look if we have a worktiem with filedata....
281+ if (fileUploadController != null ) {
282+ // check workitem... issue
283+ if (fileUploadController .getWorkitem () != null ) {
284+
285+ List <FileData > fileDataList = fileUploadController .getFileUploads ();
286+ logger .finest ("......write JSON meta data..." );
287+
288+ String result = "{ \" files\" :[" ;
289+ for (int i = 0 ; i < fileDataList .size (); i ++) {
290+
291+ FileData fileData = fileDataList .get (i );
292+ // we construct a temp file url with the current converstion id....
293+ result += "{ \" url\" : \" " + context_url + fileData .getName () + "?cid="
294+ + fileUploadController .getCID () + "\" ," ;
295+ result += "\" thumbnail_url\" : \" \" ," ;
296+ result += "\" name\" : \" " + fileData .getName () + "\" ," ;
297+ result += "\" type\" : \" " + fileData .getContentType () + "\" ," ;
298+ result += "\" size\" : " + fileData .getContent ().length + "," ;
299+ result += "\" delete_url\" : \" \" ," ;
300+ result += "\" delete_type\" : \" DELETE\" " ;
301+
302+ // last element?
303+ if (i < fileDataList .size () - 1 )
304+ result += "}," ;
305+ else
306+ result += "}" ;
307+ }
308+ result += "]}" ;
309+ out .write (result );
310+ }
311+ }
312+
313+ out .close ();
314+
315+ }
316+ }
0 commit comments