Subversion Repository Public Repository

litesoft

Diff Revisions 948 vs 950 for /trunk/Java/core/Server/src/org/litesoft/servlets/filetransfer/AbstractFileTransferServlet.java

Diff revisions: vs.
  @@ -1,187 +1,188 @@
1 - // This Source Code is in the Public Domain per: http://unlicense.org
2 - package org.litesoft.servlets.filetransfer;
3 -
4 - import org.apache.commons.fileupload.*;
5 - import org.apache.commons.fileupload.servlet.*;
6 - import org.apache.commons.fileupload.util.*;
7 - import org.litesoft.commonfoundation.typeutils.*;
8 - import org.litesoft.filesinksource.*;
9 - import org.litesoft.logger.*;
10 - import org.litesoft.util.*;
11 -
12 - import javax.servlet.*;
13 - import javax.servlet.http.*;
14 - import java.io.*;
15 - import java.util.*;
16 -
17 - public abstract class AbstractFileTransferServlet extends HttpServlet {
18 - private static final long serialVersionUID = 1L;
19 -
20 - protected static Logger LOGGER = LoggerFactory.getLogger( AbstractFileTransferServlet.class );
21 -
22 - /**
23 - * Return True for all the Content Types that we want the Browser to simply show!
24 - * <p/>
25 - * Otherwise, the Browser will be told to "ask" what to do with it!
26 - */
27 - protected boolean isDirectViewContent( String pContentType ) {
28 - return pContentType.startsWith( "image/" ) || pContentType.startsWith( "text/" );
29 - }
30 -
31 - /**
32 - * Return False for any Content Types that we are Not aware of what we want the browser to do with it!
33 - * <p/>
34 - * Will only be called if isDirectViewContent( pContentType ) return false
35 - */
36 - protected boolean isKnownContentType( String pContentType ) {
37 - return pContentType.startsWith( "application/" );
38 - }
39 -
40 - @Override
41 - public void init()
42 - throws ServletException {
43 - FileSinkAndSourceFactory.setInstance( initialize() );
44 - }
45 -
46 - abstract protected FileSinkAndSourceFactory initialize()
47 - throws ServletException;
48 -
49 - /**
50 - * Download uses GET.
51 - */
52 - @Override
53 - public void doGet( HttpServletRequest pReq, final HttpServletResponse pResp )
54 - throws ServletException, IOException {
55 - String zPathInfo = pReq.getPathInfo();
56 - String zOpaqueHandle = Strings.noEmpty( cleanFileName( zPathInfo ) );
57 - if ( zOpaqueHandle == null ) {
58 - pResp.sendError( HttpServletResponse.SC_NOT_FOUND, "Null or zero length OpaqueHandle" );
59 - return;
60 - }
61 - FileSource zFileSource = FileSinkAndSourceFactory.getInstance().createFileSource( zOpaqueHandle );
62 - FileSourceInfo zFileInfo = zFileSource.get();
63 - String zFileName = zFileInfo.getName();
64 - String zContentType = Strings.deNull( getServletContext().getMimeType( zFileName.toLowerCase() ) );
65 - pResp.setContentType( zContentType );
66 - long zSize = zFileInfo.getSize();
67 - if ( zSize < Integer.MAX_VALUE ) {
68 - pResp.setContentLength( (int) zSize );
69 - }
70 -
71 - if ( !isDirectViewContent( zContentType ) ) {
72 - pResp.setHeader( "Content-Disposition", "attachment;filename=" + zFileName ); // Only if want OS to ask disposition
73 - if ( !isKnownContentType( zContentType ) ) {
74 - LOGGER.info.log( "zName=", zFileName, " zContentType=", zContentType );
75 - }
76 - }
77 -
78 - // Caching Control
79 - pResp.addHeader( "cache-control", "max-age=10" );
80 - // We'd like to add these, but IE7 has a specific bug when downloading Microsoft Office files. See:
81 - // http://support.microsoft.com/kb/317208/
82 - // pResp.addHeader( "cache-control", "no-store" );
83 - // pResp.addHeader( "Pragma", "no-cache" );
84 -
85 - Utils.copy( zFileInfo.getInputStream(), new OutputStreamFactory() // closes InputStream
86 - {
87 - @Override
88 - public OutputStream createOutputStream()
89 - throws IOException {
90 - return pResp.getOutputStream();
91 - }
92 - } );
93 - }
94 -
95 - /**
96 - * Upload uses POST.
97 - */
98 - @Override
99 - public void doPost( HttpServletRequest pReq, HttpServletResponse pResp )
100 - throws ServletException, IOException {
101 - PrintWriter zPrintWriter = pResp.getWriter();
102 - pResp.setContentType( "application/xml" );
103 - try {
104 - insureCharacterEncoding( pReq );
105 - // Check that we have a file upload request
106 - if ( ServletFileUpload.isMultipartContent( pReq ) ) {
107 - ServletFileUpload upload = new ServletFileUpload();
108 - Map<String, String> zResults = new HashMap<String, String>();
109 - // Parse the request
110 - FileItemIterator iter = upload.getItemIterator( pReq );
111 - while ( iter.hasNext() ) {
112 - FileItemStream item = iter.next();
113 - String name = item.getFieldName();
114 - if ( !item.isFormField() ) {
115 - String zFileName = item.getName();
116 - LOGGER.debug.log( "Upload file name before clean: ", zFileName );
117 - zFileName = justFileName( zFileName );
118 - FileSink zSink = FileSinkAndSourceFactory.getInstance().createFileSink( zFileName );
119 - String zOpaqueHandle = zSink.put( item.openStream() ); // closes InputStream
120 - zResults.put( zFileName, zOpaqueHandle );
121 - } else {
122 - String zStreamAsString = Streams.asString( item.openStream() ); // closes InputStream
123 - LOGGER.warn.log( "Form field ", name, " with value ", zStreamAsString, " detected." );
124 - }
125 - }
126 -
127 - List<String> zKeys = new ArrayList<String>( zResults.keySet() );
128 - Collections.sort( zKeys );
129 -
130 - zPrintWriter.println( "<success>" );
131 - for ( String zKey : zKeys ) {
132 - String zValue = zResults.get( zKey );
133 - zPrintWriter.println( " <file name=\"" + zKey + "\" handle=\"" + zValue + "\" />" );
134 - }
135 - zPrintWriter.println( "</success>" );
136 - zPrintWriter.flush();
137 - }
138 - }
139 - catch ( Exception e ) {
140 - LOGGER.warn.log( e );
141 - zPrintWriter.println( "<fail>" );
142 - String zFailOut = Throwables.printStackTraceToString( e );
143 - zFailOut = Strings.replace( zFailOut, "<", "&lt;" );
144 - zFailOut = Strings.replace( zFailOut, ">", "&gt;" );
145 - zPrintWriter.println( zFailOut );
146 - zPrintWriter.println( "</fail>" );
147 - zPrintWriter.flush();
148 - }
149 - }
150 -
151 - private String justFileName( String pFileName ) {
152 - pFileName = "/" + cleanFileName( pFileName );
153 - return pFileName.substring( pFileName.lastIndexOf( '/' ) + 1 );
154 - }
155 -
156 - private String cleanFileName( String pFileName ) {
157 - pFileName = Strings.deNull( pFileName ).trim().replace( '\\', '/' );
158 - if ( (pFileName.length() >= 2) && Character.isLetter( pFileName.charAt( 0 ) ) && (':' == pFileName.charAt( 1 )) ) {
159 - pFileName = pFileName.substring( 2 ).trim();
160 - }
161 - while ( pFileName.startsWith( "/" ) ) {
162 - pFileName = pFileName.substring( 1 ).trim();
163 - }
164 - return pFileName;
165 - }
166 -
167 - private void insureCharacterEncoding( HttpServletRequest pReq )
168 - throws UnsupportedEncodingException {
169 - String zEnc = pReq.getCharacterEncoding();
170 - if ( zEnc != null ) {
171 - LOGGER.debug.log( zEnc, " = pReq.getCharacterEncoding();" );
172 - } else {
173 - LOGGER.debug.log( "pReq.setCharacterEncoding( \"", FileUtils.UTF_8, "\" );" );
174 - pReq.setCharacterEncoding( FileUtils.UTF_8 );
175 - }
176 - }
177 -
178 - /**
179 - * Obtain information on this servlet.
180 - *
181 - * @return String describing this servlet.
182 - */
183 - @Override
184 - public String getServletInfo() {
185 - return "File transfer servlet -- used to receive and send files";
186 - }
187 - }
1 + // This Source Code is in the Public Domain per: http://unlicense.org
2 + package org.litesoft.servlets.filetransfer;
3 +
4 + import org.apache.commons.fileupload.*;
5 + import org.apache.commons.fileupload.servlet.*;
6 + import org.apache.commons.fileupload.util.*;
7 + import org.litesoft.commonfoundation.base.*;
8 + import org.litesoft.commonfoundation.typeutils.*;
9 + import org.litesoft.filesinksource.*;
10 + import org.litesoft.logger.*;
11 + import org.litesoft.util.*;
12 +
13 + import javax.servlet.*;
14 + import javax.servlet.http.*;
15 + import java.io.*;
16 + import java.util.*;
17 +
18 + public abstract class AbstractFileTransferServlet extends HttpServlet {
19 + private static final long serialVersionUID = 1L;
20 +
21 + protected static Logger LOGGER = LoggerFactory.getLogger( AbstractFileTransferServlet.class );
22 +
23 + /**
24 + * Return True for all the Content Types that we want the Browser to simply show!
25 + * <p/>
26 + * Otherwise, the Browser will be told to "ask" what to do with it!
27 + */
28 + protected boolean isDirectViewContent( String pContentType ) {
29 + return pContentType.startsWith( "image/" ) || pContentType.startsWith( "text/" );
30 + }
31 +
32 + /**
33 + * Return False for any Content Types that we are Not aware of what we want the browser to do with it!
34 + * <p/>
35 + * Will only be called if isDirectViewContent( pContentType ) return false
36 + */
37 + protected boolean isKnownContentType( String pContentType ) {
38 + return pContentType.startsWith( "application/" );
39 + }
40 +
41 + @Override
42 + public void init()
43 + throws ServletException {
44 + FileSinkAndSourceFactory.setInstance( initialize() );
45 + }
46 +
47 + abstract protected FileSinkAndSourceFactory initialize()
48 + throws ServletException;
49 +
50 + /**
51 + * Download uses GET.
52 + */
53 + @Override
54 + public void doGet( HttpServletRequest pReq, final HttpServletResponse pResp )
55 + throws ServletException, IOException {
56 + String zPathInfo = pReq.getPathInfo();
57 + String zOpaqueHandle = ConstrainTo.significantOrNull( cleanFileName( zPathInfo ) );
58 + if ( zOpaqueHandle == null ) {
59 + pResp.sendError( HttpServletResponse.SC_NOT_FOUND, "Null or zero length OpaqueHandle" );
60 + return;
61 + }
62 + FileSource zFileSource = FileSinkAndSourceFactory.getInstance().createFileSource( zOpaqueHandle );
63 + FileSourceInfo zFileInfo = zFileSource.get();
64 + String zFileName = zFileInfo.getName();
65 + String zContentType = ConstrainTo.notNull( getServletContext().getMimeType( zFileName.toLowerCase() ) );
66 + pResp.setContentType( zContentType );
67 + long zSize = zFileInfo.getSize();
68 + if ( zSize < Integer.MAX_VALUE ) {
69 + pResp.setContentLength( (int) zSize );
70 + }
71 +
72 + if ( !isDirectViewContent( zContentType ) ) {
73 + pResp.setHeader( "Content-Disposition", "attachment;filename=" + zFileName ); // Only if want OS to ask disposition
74 + if ( !isKnownContentType( zContentType ) ) {
75 + LOGGER.info.log( "zName=", zFileName, " zContentType=", zContentType );
76 + }
77 + }
78 +
79 + // Caching Control
80 + pResp.addHeader( "cache-control", "max-age=10" );
81 + // We'd like to add these, but IE7 has a specific bug when downloading Microsoft Office files. See:
82 + // http://support.microsoft.com/kb/317208/
83 + // pResp.addHeader( "cache-control", "no-store" );
84 + // pResp.addHeader( "Pragma", "no-cache" );
85 +
86 + Utils.copy( zFileInfo.getInputStream(), new OutputStreamFactory() // closes InputStream
87 + {
88 + @Override
89 + public OutputStream createOutputStream()
90 + throws IOException {
91 + return pResp.getOutputStream();
92 + }
93 + } );
94 + }
95 +
96 + /**
97 + * Upload uses POST.
98 + */
99 + @Override
100 + public void doPost( HttpServletRequest pReq, HttpServletResponse pResp )
101 + throws ServletException, IOException {
102 + PrintWriter zPrintWriter = pResp.getWriter();
103 + pResp.setContentType( "application/xml" );
104 + try {
105 + insureCharacterEncoding( pReq );
106 + // Check that we have a file upload request
107 + if ( ServletFileUpload.isMultipartContent( pReq ) ) {
108 + ServletFileUpload upload = new ServletFileUpload();
109 + Map<String, String> zResults = new HashMap<String, String>();
110 + // Parse the request
111 + FileItemIterator iter = upload.getItemIterator( pReq );
112 + while ( iter.hasNext() ) {
113 + FileItemStream item = iter.next();
114 + String name = item.getFieldName();
115 + if ( !item.isFormField() ) {
116 + String zFileName = item.getName();
117 + LOGGER.debug.log( "Upload file name before clean: ", zFileName );
118 + zFileName = justFileName( zFileName );
119 + FileSink zSink = FileSinkAndSourceFactory.getInstance().createFileSink( zFileName );
120 + String zOpaqueHandle = zSink.put( item.openStream() ); // closes InputStream
121 + zResults.put( zFileName, zOpaqueHandle );
122 + } else {
123 + String zStreamAsString = Streams.asString( item.openStream() ); // closes InputStream
124 + LOGGER.warn.log( "Form field ", name, " with value ", zStreamAsString, " detected." );
125 + }
126 + }
127 +
128 + List<String> zKeys = new ArrayList<String>( zResults.keySet() );
129 + Collections.sort( zKeys );
130 +
131 + zPrintWriter.println( "<success>" );
132 + for ( String zKey : zKeys ) {
133 + String zValue = zResults.get( zKey );
134 + zPrintWriter.println( " <file name=\"" + zKey + "\" handle=\"" + zValue + "\" />" );
135 + }
136 + zPrintWriter.println( "</success>" );
137 + zPrintWriter.flush();
138 + }
139 + }
140 + catch ( Exception e ) {
141 + LOGGER.warn.log( e );
142 + zPrintWriter.println( "<fail>" );
143 + String zFailOut = Throwables.printStackTraceToString( e );
144 + zFailOut = Strings.replace( zFailOut, "<", "&lt;" );
145 + zFailOut = Strings.replace( zFailOut, ">", "&gt;" );
146 + zPrintWriter.println( zFailOut );
147 + zPrintWriter.println( "</fail>" );
148 + zPrintWriter.flush();
149 + }
150 + }
151 +
152 + private String justFileName( String pFileName ) {
153 + pFileName = "/" + cleanFileName( pFileName );
154 + return pFileName.substring( pFileName.lastIndexOf( '/' ) + 1 );
155 + }
156 +
157 + private String cleanFileName( String pFileName ) {
158 + pFileName = ConstrainTo.notNull( pFileName ).trim().replace( '\\', '/' );
159 + if ( (pFileName.length() >= 2) && Character.isLetter( pFileName.charAt( 0 ) ) && (':' == pFileName.charAt( 1 )) ) {
160 + pFileName = pFileName.substring( 2 ).trim();
161 + }
162 + while ( pFileName.startsWith( "/" ) ) {
163 + pFileName = pFileName.substring( 1 ).trim();
164 + }
165 + return pFileName;
166 + }
167 +
168 + private void insureCharacterEncoding( HttpServletRequest pReq )
169 + throws UnsupportedEncodingException {
170 + String zEnc = pReq.getCharacterEncoding();
171 + if ( zEnc != null ) {
172 + LOGGER.debug.log( zEnc, " = pReq.getCharacterEncoding();" );
173 + } else {
174 + LOGGER.debug.log( "pReq.setCharacterEncoding( \"", FileUtils.UTF_8, "\" );" );
175 + pReq.setCharacterEncoding( FileUtils.UTF_8 );
176 + }
177 + }
178 +
179 + /**
180 + * Obtain information on this servlet.
181 + *
182 + * @return String describing this servlet.
183 + */
184 + @Override
185 + public String getServletInfo() {
186 + return "File transfer servlet -- used to receive and send files";
187 + }
188 + }