Tuesday, December 14, 2010

File Upload

Since PAW 0.51 file upload sizes are no longer restricted. This restriction has been removed by changing the code of the underlying Brazil framework.
This post shows how file upload can be implemented.

A ready to install ZIP file containig all the code is available at the end of the post.

Since PAW 0.51 requests with content-type multipart/form-data are handled differently. The post request parameters are not handled automatically and processing is left to the application. The InputStream of the request can be accessed by using the instance variabel request.in.

The example consists of two files. One is the html file containing the form which allows the upload of two files.
The second file is the xhtml file containing the code to save the uploaded files to the /sdcard directory of the Android device.

To make the handling of the multipart form data easier the class MultipartStream from the Apache Commons Fileupload package has been added to PAW.
Actually this is an old version of the class which has no dependencies at all.

The html which contains the form is quite simple:

<html>
<head>
  <title>File Upload</title>
  <link rel="stylesheet" href="css/default.css">
</head>
<body>
  <h2>File Upload Demo</h2>
  <p>
  Uploads files to /sdcard...
  </p>

  <form action="fup.xhtml" method="post" enctype="multipart/form-data" >

  File to upload: <input name="upfile1" type="file"><br>
  File to upload: <input name="upfile2" type="file"><br>
  <input value="Upload" type="submit">
  </form>
</body>
</html>

The second file performs the following steps to save the files:
  1. Extract the boundary string from the content-type.
  2. Constructs a MultipartStream object.
  3. Skips the preamble. This ignores all data until the boundary is reached.
  4. Reads the parts until it finds a file. If a file is found it extracts the filename and stores the file in the /sdcard directory.
  5. If the part contains no file, the part's body is skipped.
There is a nice article that discribes this in more detail on oreillynet.com: Parsing form-data multiparts

So now here is the code:

<html>
<head>
<title>File Upload</title>
<link rel="stylesheet" href="css/default.css">
</head>
<body>
<bsh>
import org.paw.util.*;
import org.apache.commons.fileupload.*;

outDir = "/sdcard/fup";
files = new ArrayList();
type = (String) request.headers.get("content-type");

if (type != null &&type.startsWith("multipart/form-data")) {
 try {
  // Get the boundary string
  boundaryIndex = type.indexOf("boundary=");
  byte[] boundary = (type.substring(boundaryIndex + 9)).getBytes();

  // Construct a MultiPartStream with request.in as InputStream
  MultipartStream multipartStream =  new MultipartStream(request.in, boundary);
  boolean nextPart = multipartStream.skipPreamble();

  // Loop through all parts
  while(nextPart) {
    headers = multipartStream.readHeaders();

    // If part is a file, save it to disk. Otherwise skip it.
    if(headers.contains("filename=\"")) {
       // Get filename
       filename = headers.substring(headers.indexOf("filename=") + 10);
       filename = filename.substring(0, filename.indexOf("\""));

       // If filename is not empty save content to filesystem
       if(filename.length() > 0) {
   files.add(filename);
   multipartStream.readBodyData(new FileOutputStream(outDir + "/" + filename));
       }
                     else {
   multipartStream.discardBodyData();  
       }
    }
    else {
       multipartStream.discardBodyData();
    }

    nextPart = multipartStream.readBoundary();
  }
 }
 catch(e) {
  print(e);
 }
}
</bsh>
Uploaded files to <bsh>$$.print(outDir);</bsh>:<br>
<hr>
<bsh>
for(file : files) {
 print(file + "<br>");
}
</bsh>
</body>
</html>

Downloads:
paw_fup.zip

Installation:
Unzip into the /sdcard folder of your Android device.
Access http:<ip>:8080/ on your device. The File Upload Demo entry should be available in the list of applications.

Attention: There are no access restrictions on the installation directory!
So use this only for a short period of time or password protect the directory by defining a password (PAW Menu -> Server -> Directory Protection).

Uninstall:
Delete the folder /sdcard/paw/html/fup and the file /sdcard/paw/webconf/apps/x01_fup.conf.

No comments:

Post a Comment