- 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