Downloading and Uploading Files

 

Microsoft Corporation
October 2003

Applies to:
    Microsoft® ASP.NET
    Java Server Pages (JSP)

Summary: Learn common ways of downloading and uploading files in Java and how the Java Language Conversion Assistant (JLCA) converts Java file-handling code from Java to C#. See how JLCA-produced code can be modified to take advantage of .NET features not present in Java. (11 printed pages)

Contents

Introduction
File Download In Java
File Upload in Java
JLCA Conversion of File Download
JLCA Conversion of File Upload
ASP.NET File Download
ASP.NET File Upload
Summary

Introduction

Most Web applications involve file transfers between the client and the server. On the CodeNotes site, users can download the source code contained in various CodeNotes books, and administrators can add articles (stored as XML files) to the site for viewing by clients. In this article, we'll examine common ways of downloading and uploading files in Java. We will then examine how (or whether) the JLCA converts the file-handling code from Java to C#. Finally, we'll look at how you can modify the JLCA-produced code to take advantage of Microsoft® .NET features that are not present in Java.

Throughout this article, we will refer to file transfers from the client's perspective, so the phrase downloading a file should be interpreted as the user retrieving a file from your Web application, and the phrase uploading a file should be interpreted as the user posting a file to your Web application.

File Downloading in Java

File downloading in Java requires you to write code to send the file to the user byte-by-byte through the HttpServletResponse object. In addition to sending the file, you must also change the http content type and http header to indicate the type and name of the file that the client will be downloading.

The following example is a common practice of file downloading in a JSP page.

Listing 1. Downloading a file in Java

<% try {
   String filename = "C:\\downloadJSP\\myFile.txt";

   // set the http content type to "APPLICATION/OCTET-STREAM
   response.setContentType("APPLICATION/OCTET-STREAM");

   // initialize the http content-disposition header to
   // indicate a file attachment with the default filename
   // "myFile.txt"
   String disHeader = "Attachment;
   Filename=\"myFile.txt\"";
   response.setHeader("Content-Disposition", disHeader);

   // transfer the file byte-by-byte to the response object
   File fileToDownload = new File(filename);
   FileInputStream fileInputStream = new
      FileInputStream(fileToDownload);
   int i;
   while ((i=fileInputStream.read())!=-1)
   {
      out.write(i);
   }
   fileInputStream.close();
   out.close();
   }catch(Exception e) // file IO errors
   {
   e.printStackTrace();
}
%>

Notice that in Listing 1 there are three main steps. First, we use the response.setContentType() method to set the MIME type of the response page to APPLICATION/OCTET-STREAM. Second, we set the Content-Disposition http header to provide the default filename of myFile.txt using the response.setHeader() method. Third, we open the file and add its contents, byte-by-byte, to the response page using the out.write() method. When the client is transferred to a JSP page containing the code in Listing 1 they will be presented with the File Download dialog for their browser and be prompted whether or not they wish to save or open myFile.txt.

File Upload in Java

The J2EE and J2SE libraries do not provide direct support for uploading files, so file uploading in Java is most often controlled through third-party libraries. On the CodeNotes site, we used the Struts library to control the uploading of files by the client. Specifically, we used a FormFile and a FormBean to control file uploading. A FormBean is created the same way as a basic JavaBean, except it must extend the Struts ActionForm class. Creating a FormBean that stores a FormFile object requires you to add setXXX and getXXX methods for accessing the field that will store the uploaded file. Listing 2 shows a simple FormBean that has the single field, uploadedFile representing a FormFile.

Listing 2. An ActionForm bean containing a single FormFile object

package com.codenotes.upload;

import org.apache.struts.upload.FormFile;
import org.apache.struts.action.ActionForm;

public class UploadedFileForm extends ActionForm {
   FormFile uploadedFile;

   public void setUploadedFile(FormFile file) {
      uploadedFile = file;
   } // setUploadedFile

   public FormFile getUploadedFile() {
      return uploadedFile;
   } // getUploadedFile
} // UploadedFileForm

As previously mentioned, we must also change the struts-config.xml file to associate the FormBean with a Struts action. Two changes must be made to the struts-config.xml file, as shown in Listing 3. First, we must add a form-bean element to the<form-beans>element. This new<form-bean/>element will provide the class name of our FormBean and the name we will use to reference the FormBean throughout the rest of struts-config.xml. Second, we have to associate the FormBean with a struts action. In this example, we will associate the FormBean with the UploadAction class, which we will partially define near the end of this section.

Listing 3. Associating a FormBean with an action in the struts-config.xml file

<!-- start of struts-config.xml omitted for brevity -->
<form-beans>
   <!-- provide the name and classname for our FormBean -->
   <form-bean name="uploadForm"
   type="com.codenotes.upload.UploadedFileForm"/>
</form-beans>

<action-mappings>
   <!-- associate the form with the UploadAction class which
   we will define later. -->
   <action path="/upload"
      name="uploadForm"
      scope="session"
      type="com.codenotes.upload.UploadAction">
      <forward name="uploadResult" path="/jsp/uploadResult.jsp"/>
   </action>
<!-- the rest of the struts-config is omitted for brevity -->

The bolded lines indicate that we have associated the FormBean with the name uploadForm to the /upload action. Because of the form-bean element that we added at the beginning of the struts-config.xml file, uploadForm is mapped to an instance of the UploadedFileForm class defined in Listing 2. Setting the scope attribute to session indicates that the values for this FormBean should be retained throughout the client's entire visit to the site. The alternative value for scope is request, which will only retain values for the bean during the individual client request.

Once the FormBean has been associated with an action, we need to use the Struts html tag library in our JSP page in order to have the struts engine update the FormBean. In order to map the entries of the HTML form to the properties in the FormBean, the form element, and all its associated input elements, must be created using the Struts HTML tag library's form element, and the respective Struts HTML input elements. A sample form using the UploadedFileForm is shown in Listing 4.

Listing 4. A Web form for uploading a file

<%@page language="java"%>
<%@taglib uri="/WEB-INF/struts-html.tld" prefix="html"%>

<html:html>
<html:form enctype="multipart/form-data"action="/upload">

<h1>Add Article</h1>
   Please enter file you wish to add:
   <br><br>
   <html:file property="uploadedFile"/>
   <br><br>
   <html:submit value="Upload File"/>
</html:form>

</html:html>

Notice that in Listing 4, we used the prefixhtml:to refer to all elements from the Struts html tag library. This allowed us to create a Struts HTML form element by usinghtml:formand a Struts HTML file element using html:file.

Now in our UploadAction class, we can use the FormBean to retrieve information from the file uploaded to the form in Listing 4. However, as shown in Listing 5 we must first cast the form parameter to an UploadedFileForm in the execute() method of the UploadAction class. The Struts FormFile class allows you to perform various functions to retrieve information from the uploaded file through methods such as: getFileSize(), getInputStream(), and getFileName(). In the UploadAction class in Listing 5, we use the getFileName() and getInputStream() to save the file from the form in Listing 4 to disk.

Listing 5. Saving an uploaded file to disk using Struts

public ActionForward
perform(ActionMapping mapping,
   ActionForm form, 
   HttpServletRequest request, 
   HttpServletResponse response)
throws IOException, ServletException {

   UploadedFileForm fileForm = (UploadedFileForm)form;
   FileForm uploadedFile = fileForm getUploadedFile();
   InputStream uploadInStream = uploadedFile.getInputStream();
   String filename = uploadedFile.getFileName();
   FileOutputStream fOut = new FileOutputStream("c:\\uploads\\"
      + filename);
   int c=0;
   while (c=uploadInStream.read() != -1) {
      fOut.write(c);
   } // while
   fOut.flush();
   fOut.close();
   //  perform the rest of the action here
}

JLCA Conversion of File Download

When the JLCA converts a JSP page containing file-downloading code, the resultant Microsoft ASP.NET page will be very close to being functionally equivalent. For our sample JSP page, the JLCA produced the ASP.NET code shown in Listing 6.

Listing 6. The JLCA produced .aspx page for file download

<%
   try
   {
      System.String filename = "C:\\downloadJSP\\myFile.txt";

      // set the http content type to "APPLICATION/OCTET-STREAM
      Response.ContentType = "APPLICATION/OCTET-STREAM";

      // initialize the http content-disposition header to
      // indicate a file attachment with the default filename
      // "myFile.txt"
      System.String disHeader = "Attachment;
      Filename=\"myFile.txt\"";
      Response.AppendHeader("Content-Disposition", disHeader);

      // transfer the file byte-by-byte to the response object
      System.IO.FileInfo fileToDownload = new
         System.IO.FileInfo(filename);
      System.IO.FileStream fileInputStream = new
         System.IO.FileStream(fileToDownload.FullName,
         System.IO.FileMode.Open, System.IO.FileAccess.Read);

      int i;
      while ((i = fileInputStream.ReadByte()) != - 1)
      {
         Response.Write(i);
      }
      fileInputStream.Close();
      Response.Flush();
      Response.Close();
   }
   catch (System.Exception e)
   // file IO errors
   {
      SupportClass.WriteStackTrace(e, Console.Error);
   }
%>

Only one line needed to be changed in the converted code in order for it to work properly. The bolded line in Listing 6 needed to be changed fromResponse.Write(i)to Response.Write((char)i). This is because we are transferring text files for download and without casting to a char, the downloaded file was containing the ASCII codes for the characters as opposed to the characters themselves. Even if you are not making text files available for download, you may have to change the way the file is written byte-by-byte, so it is important that you test your file upload application for each possible file that may be downloaded. The corrected file-transfer .NET code is shown in Listing 7.

Listing 7. The corrected version for file download

<%
   try
   {
      System.String filename = "C:\\downloadJSP\\myFile.txt";

      // set the http content type to "APPLICATION/OCTET-STREAM
      Response.ContentType = "APPLICATION/OCTET-STREAM";

      // initialize the http content-disposition header to 
      // indicate a file attachment with the default filename
      // "myFile.txt"
      System.String disHeader = "Attachment;
      Filename=\"myFile.txt\"";
      Response.AppendHeader("Content-Disposition", disHeader);

      // transfer the file byte-by-byte to the response object
      System.IO.FileInfo fileToDownload = new
         System.IO.FileInfo(filename);
      System.IO.FileStream fileInputStream = new
        System.IO.FileStream(fileToDownload.FullName,
        System.IO.FileMode.Open, System.IO.FileAccess.Read);
      int i;
      while ((i = fileInputStream.ReadByte()) != - 1)
      {
         Response.Write((char)i);
      }
      fileInputStream.Close();
      Response.Flush();
      Response.Close();
   }
   catch (System.Exception e)
   // file IO errors
   {
      SupportClass.WriteStackTrace(e, Console.Error);
   }
%>

JLCA Conversion of File Upload

Because the majority of Java-based Web applications use third-party software to control the uploading of files, the JLCA will not convert most of your file uploading code, requiring you to do the re-writing manually. Depending on your third-party software, you may also have to redesign portions of your site. For example, on the CodeNotes site, we used an ActionForm bean with a FormFile property to represent uploaded files. Because we can no longer use Struts in the .NET version of the CodeNotes site, we not only had to replace the FormFile-related code, we also had to redesign the way the HTML form was processed, replacing the Struts code that automatically updated the form. However, as we will soon discuss, the HttpRequest class makes it very easy to retrieve uploaded files and the HttpPostedFile class provides all the functionality we required of the FormFile class, so the changes that were required were neither difficult nor lengthy to implement.

ASP.NET File Download

Downloading a file in ASP.NET is straightforward and does not require you to copy the file byte-by-byte to the response page, although, as shown in Listing 7, byte-by-byte transfer is still possible if you require the contents of the file to be changed at run-time. Not having to copy the file byte-by-byte will eliminate the casting problems we encountered in the JLCA Conversion of File Download section. You must still set the http content type to indicate the file is for downloading, but instead of transferring the file byte-by-byte, you can simply call the HttpResponse.WriteFile() method to transfer the file as shown in Listing 8.

Listing 8. Downloading a file in ASP.NET

  <%
try
{
   System.String filename = "myFile.txt";

   // set the http content type to "APPLICATION/OCTET-STREAM
   Response.ContentType = "APPLICATION/OCTET-STREAM";
   
   // initialize the http content-disposition header to
   // indicate a file attachment with the default filename
   // "myFile.txt"
   System.String disHeader = "Attachment; Filename=\"" + filename +
      "\"";
   Response.AppendHeader("Content-Disposition", disHeader);

   // transfer the file byte-by-byte to the response object
   System.IO.FileInfo fileToDownload = new
      System.IO.FileInfo("C:\\downloadJSP\\DownloadConv\\myFile.txt");
   Response.Flush();
   Response.WriteFile(fileToDownload.FullName);}
catch (System.Exception e)
// file IO errors
{
   SupportClass.WriteStackTrace(e, Console.Error);
}
%>

Notice that in Listing 8 as in Listing 1 there are still three basic steps to downloading a file. First, the http content type is set to APPLICATION/OCTET-STREAM. Second, the Content-Disposition header is set to indicate an attachment and the default file name. However, unlike Listing 1 in the third step, the file is not sent byte-by-byte, but instead a single line sends the file contents.

ASP.NET File Upload

Uploading a file in ASP.NET is very straightforward. You have to add the File Field html server control to your Web form. Once you have added the File Field control to your HTML form you need to make sure that the form has themethod="post"andenctype="multipart/form-data"attributes in order to be able to access the posted file. Listing 9 shows a sample form that accepts a single file from the user and a button to submit the form.

Listing 9. A form for uploading a file

<form id="FileUploadForm" method="post"
   enctype="multipart/form-data" action="afterUpload.aspx">
   <P><INPUT type="file" id="UploadedFile" runat="server"></P>
   <P><INPUT type="submit" value="Submit"></P>
</form>

Once the user has added the file to the form and clicked the Submit button, you can access the posted file through the HttpRequest object. All uploaded files for the current request are stored in the HttpRequest.Files property. The Files property is an HttpFileCollection object that acts similarly to a Hashtable: a String is used as a key and the value returned is an HttpPostedFile object. The HttpPostedFile class provides similar functionality to that provided by the FormFile class in Struts. This functionality is provided through various properties in the HttpPostedFile class such as the FileName property, which returns the uploaded file's name, including directories, on the client's computer; the InputStream property, which returns a Stream object containing the file data; and the ContentLength property which returns the length in bytes of the uploaded file. Unlike the FormFile class, the HttpPostedFile class also contains a SaveAs() method which stores the file on your server with the given file name. Listing 10 contains an example of how to use the HttpPostedFile class to save an uploaded file to disk using the original file name. Because this code requires access to the HttpRequest object containing information about the form, this code will be placed in the Page_Load() method of the next ASP.NET page or any other code that will be executed after the form has been submitted, but before the next ASP.NET page is displayed. For example, the code in Listing 10 could be placed in the Page_Load() method of the afterUpload.aspx page, which is referenced by the action attribute in Listing 9.

Listing 10. Saving an uploaded file to disk

HttpFileCollection allFiles = Request.Files;
HttpPostedFile uploadedFile = allFiles["UploadedFile"];
FileInfo uploadedFileInfo = new 
   FileInfo(uploadedFile.FileName);
uploadedFile.SaveAs("C:\\UploadedFiles\\" +
   uploadedFileInfo.Name);
userMessage = "The file has been svaed with the filename: " +
   uploadedFileInfo.Name;

Summary

The basic process for a file download (from server to client) can be the same for both JSP and ASP.NET. In fact, the JLCA will properly convert your JSP code with a few minor changes. However, the process for uploading a file may be totally different, especially if you are using Struts on the JSP side. ASP.NET has a built-in control specifically for uploading files that replaces the Struts functionality. In other words, with a file upload, you have a case where replacing JSP code with a built in ASP.NET feature is faster, easier and better than trying to make a conversion work properly.

© Microsoft Corporation. All rights reserved.