- From: Roland Mainz <Roland.Mainz@informatik.med.uni-giessen.de>
- Date: Mon, 22 May 2000 18:19:00 +0200
- To: "'www-jigsaw@w3.org'" <www-jigsaw@w3.org>
- Message-Id: <39295DF4.99E6AB32@informatik.med.uni-giessen.de>
Debraj Das wrote: > I've been using the following code (inspired by Roland Mainz's file > transfer servlet code). > I can recieve the file with no problems, but the file does not > retain it's original name. > > in the example I have tried to send the file notepad.exe, but when > my browser prompts me to save the file, the original name > notepad.exe is not present. I am prompted to download > TestFileXferServlet (i.e. the name of my servlet). I can download > the file, rename it to notepad.exe (manually) and then run the file. > > How can I make it so that I retain the original file name during > download? > > public void doPost(HttpServletRequest req, > HttpServletResponse res)throws javax.servlet.ServletException > {try{ > System.out.println("sending file1.txt"); > FileInputStream in = new > FileInputStream("c:\\winnt\\notepad.exe"); > > > res.setContentType("application/x-www-form-urlencoded"); Uhhg... I don't know whether the idea with x-www-form-urlencoded is a good one... I've attached the current source of my SnoopServlet which runs here for a long time without such problems (except in Win95... but an update to a newer browser version should fix this problem...). Note that I'll update this beast in the near future by a JSP-driven engine (or something which uses a XML-based config) to get rid of several problems (like that huge configs can be a pain to manage. The biggest config here has 474 entries. Ouch...). ---- Bye, Roland -- __ . . __ (o.\ \/ /.o) Roland.Mainz@informatik.med.uni-giessen.de \__\/\/__/ gisburn@informatik.med.uni-giessen.de /O /==\ O\ MPEG specialist, C&&JAVA&&Sun&&Unix programmer (;O/ \/ \O;) TEL +49 641 99-13193 FAX +49 641 99-41359
package de.gis.www.servlets.download; import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; /** * download area servlet * * @version 1.54 * @author Roland Mainz */ public class DownloadServlet extends HttpServlet { // Class describing a single entry in out "download"-database class DownloadItem { String reference; // "reference=" config-file internal reference String description; // "descr"= small, one-line description String label; // "label=" label, e.g. link text String filename; // "file=" path/filename on local disk String filepart; // filename-only part of filename String contenttype; // "contenttype=" mime-type of file String user; // "user=" if given, only include download item if session user is the same long filesize; // file size in bytes DownloadItem() { // defaults description = "n/a"; contenttype = "application/octet-stream"; user = null; } // check wheter the user is allowed to download this item boolean checkUser( String requestUser ) { // this download item has no user-access restriction ? if( user == null ) return( true ); if( requestUser == null ) { // download item has user access restriction, but we did not get any user info (anonymous access return( false ); } return( user.equalsIgnoreCase( requestUser ) ); } boolean matchReference( String match ) { return( reference.compareTo( match ) == 0 ); } } // Class describing a section in the sections database class SectionItem extends Vector { String title; SectionItem() { super(); } boolean hasEntriesForUser( DownloadServlet parent, String requestUser ) { for( int i = 0 ; i < size() ; i++ ) { DownloadItem di = parent.findDownLoadItemByRef( (String)get( i ) ); if( di == null ) continue; if( di.checkUser( requestUser ) ) return( true ); } return( false ); } } // Global vars (set up by init()) String configfile_name = null; long configfile_last_scanned = 0L; File configfile = null; Vector downloadable_stuff = new Vector(); Vector sections = new Vector(); // also used to syncronize threads during updates... public void init( ServletConfig config ) throws ServletException { // this MUST be called first super.init( config ); d( "## init " ); // get configuration parameters configfile_name = config.getInitParameter( "CONFIGFILE" ); if( configfile_name == null ) throw new ServletException( "no CONFIGFILE parameter" ); synchronized( sections ) { buildIndex(); } } private void updateIndex() throws ServletException { synchronized( sections ) { boolean update_required; update_required = (configfile != null)?(configfile_last_scanned < configfile.lastModified()):(true); // update required ? if( update_required ) { d( "# updating index..." ); // throw the old rubbish away... downloadable_stuff.removeAllElements(); sections.removeAllElements(); // .. and build new info buildIndex(); } } } private void buildIndex() throws ServletException { try { // Note: We're silently loosing here the File object reference from any "old" config file... configfile = new File( configfile_name ); LineNumberReader in = new LineNumberReader( new FileReader( configfile ) ); String s; // loop until end-of-header... while( (s = in.readLine()) != null ) { // ignore comments if( s.startsWith( "#" ) || s.startsWith( ";" ) || s.startsWith( "//" ) || (s.length() < 2) ) continue; // d( "processing line '" + s + "'" ); StringTokenizer st = new StringTokenizer( s, ";" ); try { String type = st.nextToken(); if( type == null ) throw new IllegalArgumentException( "bad token (type identifier) in config file" ); // is this a section ? if( type.equals( "section" ) ) { SectionItem si = new SectionItem(); si.title = st.nextToken(); if( si.title == null ) throw new IllegalArgumentException( "bad token (title expected) in config file" ); while( st.hasMoreElements() ) { si.add( st.nextToken() ); } sections.add( si ); } // is this a file to download ? else if( type.equals( "file" ) ) { DownloadItem di = new DownloadItem(); while( st.hasMoreElements() ) { String element = st.nextToken(); if( element == null ) throw new IllegalArgumentException( "bad token (element expected)" ); if( element.startsWith( "ref=" ) ) di.reference = element.substring( 4 ); if( element.startsWith( "label=" ) ) di.label = element.substring( 6 ); if( element.startsWith( "descr=" ) ) di.description = element.substring( 6 ); if( element.startsWith( "file=" ) ) di.filename = element.substring( 5 ); if( element.startsWith( "contenttype=" ) ) di.contenttype = element.substring( 12 ); if( element.startsWith( "user=" ) ) di.user = element.substring( 5 ); } // d( "got: '" + di.label + "','" + di.filename + "','" + di.contenttype + "'" ); // asserts, defaults, 1st level if( di.filename == null ) throw new IllegalArgumentException( "missing filename" ); if( di.filename.length() < 2 ) throw new IllegalArgumentException( "filename to small" ); if( di.contenttype.length() < 2 ) throw new IllegalArgumentException( "??? content type" ); // asserts, 2nd level File downloadfile = new File( di.filename ); di.filepart = downloadfile.getName(); di.filesize = downloadfile.length(); if( !downloadfile.exists() ) throw new IllegalArgumentException( "file does not exsist" ); if( !downloadfile.isAbsolute() ) throw new IllegalArgumentException( "cannot export non-files with non-absolute filenames" ); // defaults if( di.reference == null ) di.reference = di.filepart; if( di.label == null ) di.label = di.filepart; downloadable_stuff.add( di ); } else { throw new IllegalArgumentException( "line does not start with 'file' or 'section'" ); } } catch( IllegalArgumentException exc ) { d( "line " + in.getLineNumber() + " {" + s + "} rejected:" + exc ); } } /* Seems to be a successfull index build. * configfile_last_scanned is updated here to mark that the data loaded in this object are in sync with the configfile... */ configfile_last_scanned = configfile.lastModified(); d( "## ready" ); } catch( Exception exc ) { d( "init exception: " + exc ); exc.printStackTrace(); throw new ServletException( "buildIndex failed" ); } } public void destroy() { d( "## destroy" ); } // debugging output static private void d( String s ) { System.err.println( "Download[" + Thread.currentThread().getName() + "]: " + s ); } public void doGet( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException { d( "access from " + req.getRemoteUser() + "@" + req.getRemoteHost() + " (" + req.getRemoteAddr() + ")" ); // debugging only (my debugging offline message) if( 1 != 1 ) { PrintWriter out; res.setContentType( "text/html" ); out = res.getWriter(); out.println( "<html><body><h1>Download area offline due internal problems</h1></body></html>" ); out.close(); return; } // no article requested ? - Then send the index page... if( (req.getPathInfo() == null) || req.getPathInfo().equals( "/" ) || req.getPathInfo().startsWith( "#" ) ) { // check for updates... updateIndex(); doGetIndex( req, res ); } else { doGetFile( req, res ); } } private void doGetIndex( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException { String requestURI = req.getRequestURI(); if( !requestURI.endsWith( "/" ) ) { // the main page should act as a "directory", therefore we want a '/' at the end ! res.sendRedirect( new String( javax.servlet.http.HttpUtils.getRequestURL( req ) + "/" ) ); return; } String requestUser = req.getRemoteUser(); PrintWriter out; res.setContentType( "text/html" ); out = res.getWriter(); out.println( "<html>" ); out.println( "<head>" ); out.println( "<title>Download Servlet output</title>" ); out.println( "<meta name=\"Author\" content=\"Roland Mainz\">" ); out.println( "<meta name=\"GENERATOR\" content=\"DownloadServlet/1.54\">" ); out.println( "</head>" ); // begin body and set page colors out.println( "<body text=\"#000000\" bgcolor=\"#FFFFFF\" link=\"#0000EE\" vlink=\"#511A8B\" alink=\"#FF0000\">" ); out.println( "<h1>Downloadable stuff...</h1>"); out.println( "This page contains some usefull stuff used within our institute.<br>" ); if( requestUser == null ) { out.println( "We also have the <a href=\"/download_imi/\">same download area password protected</a> with more internal stuff.<br>" + "Please be patient with the page layout; it is managed and updated automatically by our \"Download servlet\" daemon.<br>" ); } out.println( "<br>" ); out.flush(); // sync with any update in progress on another thread... synchronized( sections ) { printJumpLine( out, requestUser ); out.println( "<table border width=\"98%\">" ); out.println( "<tr><td><font size=\"+1\"><strong>Description</strong></font></td>" + "<td><font size=\"+1\"><strong>File</strong></font></td>" + "<td><font size=\"+1\"><strong>Size</strong></font></td></tr>" ); // scan our table if sections for( int i = 0 ; i < sections.size() ; i++ ) { SectionItem si = (SectionItem)sections.get( i ); // check whether the title line of this section has been written or not // this is used to avoid that a title is written for an empty section (maybe all items are inacessible) boolean wroteTitle = false; for( int j = 0 ; j < si.size() ; j++ ) { String ref = (String)si.get( j ); DownloadItem di = findDownLoadItemByRef( ref ); if( di != null ) { if( !di.checkUser( requestUser ) ) continue; if( !wroteTitle ) { // section title and HTML target out.println( "<tr><td><strong><a name=\"" + java.net.URLEncoder.encode( si.title ) + "\">" + si.title + "</a></strong></td></tr>" ); wroteTitle = true; } out.println( "<tr>" ); out.print( " <td bgcolor=\"#F0F0F0\">" + di.description + "</td>" ); out.print( " <td bgcolor=\"#FFFF00\">" + "<a href=\"" + requestURI + java.net.URLEncoder.encode( di.filepart ) + "\">" + di.label + "</a>" + "</td>" ); out.print( " <td bgcolor=\"#C0C0C0\">" + di.filesize + " bytes" + "</td>" ); out.println( "</tr>" ); } else { d( "section item not found: " + si.title + "," + ref ); } } } out.println( "</table><br>" ); } out.flush(); // give the browser all info to build the table... printJumpLine( out, requestUser ); // print our footer... (got from the MailinglistServlet.class) // this is now a "strip-down" of the XML footer: // simply copy-paste, removing all "html:" namespace identifies from the tags and removeing the </br> tags out.println( " <br><p/><hr/><p/>" ); out.println( " <a href=\"http://www.w3.org/jigsaw/\"><img src=\"/icons/jigpower\" alt=\"Powered by Jigsaw [2.0.4]webserver.\" border=\"0\" align=\"abscenter\"></img></a>" ); out.println( " <br><font size=\"-1\">Written by <a href=\"mailto:jigsaw@informatik.med.uni-giessen.de\">Roland Mainz, IMI</a></font><br><font size=\"-1\">Last updated 04.04.2000</font><br>" ); out.println("</body></html>"); out.close(); } private void printJumpLine( PrintWriter out, String requestUser ) { // scan our table if sections and build a small index line if( sections.size() > 1 ) { out.print( "<font size=\"-2\">[ " ); int num = 0; for( int i = 0 ; i < sections.size() ; i++ ) { SectionItem si = (SectionItem)sections.get( i ); if( !si.hasEntriesForUser( this, requestUser ) ) continue; if( num++ > 0 ) out.print( " | " ); out.print( "<a href=\"#" + java.net.URLEncoder.encode( si.title ) + "\">" + si.title + "</a>" ); } out.println( " ]</font><br><br>" ); } } private DownloadItem findDownLoadItemByFilePart( String match ) { // scan our table if "indexed" downloadable files, if we got a match... for( int i = 0 ; i < downloadable_stuff.size() ; i++ ) { DownloadItem di = (DownloadItem)downloadable_stuff.get( i ); // match ? if( di.filepart.compareTo( match ) == 0 ) { return( di ); } } return( null ); } private DownloadItem findDownLoadItemByRef( String match ) { // scan our table if "indexed" downloadable files, if we got a match... for( int i = 0 ; i < downloadable_stuff.size() ; i++ ) { DownloadItem di = (DownloadItem)downloadable_stuff.get( i ); // match reference ? if( di.matchReference( match ) ) { return( di ); } } return( null ); } // number of concurrent downloads static int maxUsers = 6; // start a download synchronized protected boolean startNewDownload() { if( maxUsers > 0 ) { maxUsers--; return( true ); } else { return( false ); } } // download has been finished synchronized protected void finishDownload() { maxUsers++; } private void doGetFile( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException { DownloadItem di = null; String path = req.getPathInfo().substring( 1 ); // get path, and get rid if it's leading '/' String requestUser = req.getRemoteUser(); // note that jigsaw does the proper URL decoding seems... d( "file requested: '" + path + "'" ); /* sync with a possible update in progress * An update may "invalidate" a downloadable file, but this doesn't hurt - * in that case we return a "file not found" :-) * The download itself does not need to be syncronized, because a DownloadItem * contains only const data. */ synchronized( sections ) { di = findDownLoadItemByFilePart( path ); } // if we found a file... if( di != null ) { if( di.checkUser( requestUser ) ) { // start a new download (if enougth connections are free) if( startNewDownload() ) { try { try { putFile( di, res ); // log every successfull download ! log( "transfer to " + req.getRemoteUser() + "@" + req.getRemoteHost() + " (" + req.getRemoteAddr() + ") of " + di.filename + " successfull." ); } catch( IOException exc ) { d( "putFile IO err: " + exc ); throw exc; } } finally { finishDownload(); } } else { res.sendError( res.SC_SERVICE_UNAVAILABLE, "too many users - try again later" ); d( "download rejected - too many users" ); } } else { res.sendError( res.SC_FORBIDDEN, "access restricted" ); d( "download rejected - user " + requestUser + " not authorized." ); } } else { res.sendError( res.SC_NOT_FOUND, "file not found" ); } } private void putFile( DownloadItem di, HttpServletResponse res ) throws IOException { int sentsize = 0; d( "sending '" + di.filename + "' (" + di.contenttype + ")" ); // get source // InputStream in = new FileInputStream( di.filename ); InputStream in = new BufferedInputStream( new FileInputStream( di.filename ) ); // prepare and get destination res.setContentType( di.contenttype ); res.setContentLength( (int)di.filesize ); // seems that here sits a possible problem for files >= 2 GB... OutputStream out = res.getOutputStream(); /* int i; while( (i = in.read()) != -1 ) { out.write( i ); sentsize++; } */ int readlen; byte buffer[] = new byte[ 256 ]; // loop until EOF while( (readlen = in.read( buffer )) != -1 ) { // throws IOException: broken pipe when download is canceled. out.write( buffer, 0, readlen ); sentsize += readlen; } // Success ! Close streams. out.flush(); out.close(); in.close(); // log what happened... d( "transfer of " + di.filename + " (" + di.filesize + "/" + sentsize + ") done." ); } }
Received on Monday, 22 May 2000 12:19:11 UTC