Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning Apache Struts - From Novice To Professional (2006)

.pdf
Скачиваний:
56
Добавлен:
17.08.2013
Размер:
11.68 Mб
Скачать

128C H A P T E R 1 0 M O R E T A G S

An EL tutorial from Sun: http://java.sun.com/j2ee/1.4/docs/tutorial/doc/ JSPIntro7.html

A list of Struts tags that have not been EL-enabled because they have equivalents in JSTL: http://struts.apache.org/struts-doc-1.2.x/faqs/struts-el.html

Summary

The Logic library has tags for iteration, conditional processing, and flow control. Most of these tags have JSTL equivalents.

The Nested tag library is used to shift the implicit base object that the property attribute refers to. You can use nested properties to achieve a similar effect.

JSTL consists of four tag libraries (Core, Formatting, XML Processing, and Database Access), which you can use in place of some Struts tags.

<c:forEach>, <c:if>, <c:choose>...<c:when>, and <c:out> tags from the JSTL core tag library can be used to replace the Struts <logic:iterate> and conditional processing tags in the Logic tag library.

<c:out> can be used to replace <bean:write>, but supporting tags from the JSTL Formatting tag library are needed to fully replace <bean:write>.

EL is a way to give EL-enabled tags dynamic attributes. EL has several special keywords to help it do this.

There are EL-enabled Struts tags you can use alongside the original Struts tags.

C H A P T E R 1 1

■ ■ ■

Uploading Files

In this chapter, I’ll discuss a few techniques you can use to upload files to a server. If you’ve ever tried to create this functionality using JSP and servlets, you’ll know that it isn’t easy.

Struts gives you a really painless way to upload files. But before I show you how, it is instructive to understand a little bit of what goes on behind the scenes when a user attempts to upload a file.

The starting point for any webapp that allows file uploads is the HTML element for uploading a file:

<input type="file">

In most graphical browsers, this gets rendered as a text input field followed by a Browse button, as depicted in Figure 11-1.

Figure 11-1. The input field for file uploading as displayed in Mozilla

Users can either type the filename directly in the given field, or click the Browse button, which launches a dialog box allowing the user to visually navigate to the desired file.

When the user clicks the submit button of the enclosing form, the web browser reads the file and encodes each byte with a suitable encoding, like uuencode (see “Useful Links”), and sends this to the server, along with any posted form data. Depending on its length, the file might also be broken into pieces.

So, seen from the server end the uploaded data is encoded and broken into several parts. To reconstruct the original file, you’d have to correctly parse the input data, rejoining broken parts, and finally decode everything. Ouch!

Struts makes things much, much easier. First, on the JSP page, you put in an

<html:file> tag:

<html:file property="myFile"/>

129

130

C H A P T E R 1 1 U P L O A D I N G F I L E S

As usual, the property attribute corresponds to a property on your ActionForm subclass that accepts the uploaded file. In the previous snippet, that property name is myFile.

The second change you make is to add a special property on the enclosing <html:form>:

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

This new property tells Struts that the data submitted by the form will be encoded and in parts.

Next, on your ActionForm subclass, you’ll have to declare a property to accept the file data, as shown in Listing 11-1.

Listing 11-1. Snippet of MyActionForm

import org.apache.struts.upload.FormFile;

public class MyActionForm extends ActionForm{

protected FormFile _file;

public FormFile getMyFile(){ return _file;

}

public void setMyFile(FormFile f){ _file = f;

}

... //rest of MyActionForm

In Listing 11-1, FormFile is the Struts class that handles access to the downloaded file. The fully qualified class name is

org.apache.struts.upload.FormFile

FormFile has two important functions:

getInputStream(): Returns an InputStream to the downloaded file. You use this to read the file itself.

destroy(): Deletes the downloaded file. The downloaded file is saved in a temporary directory, so be sure to call destroy() to free up disk space.

FormFile has a few other functions; these are listed in Appendix B.

C H A P T E R 1 1 U P L O A D I N G F I L E S

131

Uploading a Fixed Number of Files at Once

From our discussion so far, it should be clear that since the uploaded file is just a property of the ActionForm, you can upload more than one file on a single form. If the files are to be treated equally, the best way to do this is to use indexed properties, as shown in Listing 11-2.

Listing 11-2. A Simple Form to Upload a Fixed Number of Files

<%@ taglib uri="/tags/struts-html" prefix="html" %> <html>

<body>

<html:form enctype="multipart/form-data" action="/FileUpload.do">

<h1>Upload Four Files</h1>

File 1:<html:file property="file[0]" size="20"/><br> File 2:<html:file property="file[1]" size="20"/><br> File 3:<html:file property="file[2]" size="20"/><br> File 4:<html:file property="file[3]" size="20"/> <html:submit/><br>

</html:form>

</body>

</html>

The ActionForm associated with Listing 11-2 is described in Listing 11-3.

Listing 11-3. ActionForm Associated with Listing 11-2

import org.apache.struts.upload.FormFile;

public class FileUploadForm extends ActionForm{

protected FormFile[] _files;

public FileUploadForm(){ _files = new FormFile[4];

}

public FormFile getFile(int i){ return _files[i];

}

132

C H A P T E R 1 1 U P L O A D I N G F I L E S

public void setFile(int i, FormFile f){ _files[i] = f;

}

... //rest of FileUploadForm

One important thing to note from Listing 11-3 is the order of the arguments in setFile(). A common mistake is to reverse the order:

setFile(FormFile f , int i) //WRONG!! Arguments reversed.

Apart from this, Listings 11-2 and 11-3 should hold no surprises.

It is possible to improve this technique somewhat if the order in which the files are specified for uploading is unimportant. Listing 11-4 is a variation of Listing 11-3’s

FileUploadForm.

Listing 11-4. A Variation on Listing 11-3

import java.util.*;

import org.apache.struts.action.*; import org.apache.struts.upload.*;

public class FileUploadForm extends ActionForm {

protected List _files;

public FileUploadForm(){ _files = new ArrayList();

}

public FormFile getFile(int i){

return(_files.size() > i)? (FormFile)_files.get(i) : null;

}

public void setFile(int i, FormFile f){ if(f.getFileSize() <= 0){

f.destroy();

}else{ _files.add(f);

}

}

}

C H A P T E R 1 1 U P L O A D I N G F I L E S

133

The advantage of Listing 11-4 is that you don’t have to hard-code the number of files to be uploaded into the ActionForm subclass. Notice also that setFile() only accepts a file of nonzero length.

The downside of this approach is that there’s no guarantee Struts will call the setFile() function in either ascending or descending order of the index variable. This means that subsequent retrievals of the uploaded files might not be in the order specified by the user.

The main drawback with both these simple techniques is that you have to know beforehand the number of files to be uploaded. Even with the second technique, the number of <html:file> tags had to be hard-coded in the JSP. There’s no way for a user to upload an arbitrary number of files. In some applications (e.g., a webmail app), this might be too restrictive. I’ll show you how to overcome this shortcoming next.

RESTRICTING THE SIZE OF UPLOADED FILES

In Chapter 9, I described how you can limit the maximum allowable file size to accept in a single upload. You use the <controller> tag of struts-config.xml to do this. For example:

<controller maxFileSize="2M" />

sets a limit of 2MB. You use a numeric value followed by K, M, or G to indicate a size in kilobytes, megabytes, or gigabytes.

Uploading Any Number of Files

Struts does not provide a one-stop solution for uploading an arbitrary number of files. However, it does provide adequate tools for you to “roll your own” solution. In this section, I’ll describe one solution to this interesting problem. (For expositional clarity, the solution I’ll describe will not preserve the order of the uploaded files, but it isn’t too difficult to fix this.)

The heart of the solution is to have two submit buttons for the file upload form:

The first submit button is an Upload More Files button (or something equivalent, like a link). This button simply forces Struts to save the uploaded files, and then redisplays the input page with the list of files uploaded thus far. The values keyed in on other fields are also displayed. Because this round-trip may take time, it is usually advisable to give the user more than one input field to upload files.

The second button is the true submit button, which sends all the form data (including all uploaded files) for processing.

134

C H A P T E R 1 1 U P L O A D I N G F I L E S

In what follows, I’ll illustrate the solution in the context of a trivial webmail app. This webapp has just two pages: the first allows the user to compose an email message, and the second page displays the contents of the email. Figure 11-2 shows what the compose page looks like, while Figure 11-3 depicts the output page.

Figure 11-2. The compose page

Figure 11-3. The output page (showing six attached files)

C H A P T E R 1 1 U P L O A D I N G F I L E S

135

Struts has a robust way for you to put two submit buttons on a single form. I’ve described this technique in detail in Chapter 17’s LookupDispatchAction. Please read that section before proceeding. In what follows, I’ll assume that you know how LookupDispatchAction works.

The starting point of the solution is Listing 11-4’s FileUploadForm. This ActionForm subclass contains all the functionality we need to upload an arbitrary number of forms. In order to accommodate the other fields of the email, we subclass FileUploadForm, as in Listing 11-5. This is good design since we can reuse the file uploading functionality in other forms.

Listing 11-5. WebmailForm.java

package net.thinksquared.webmail;

import javax.servlet.http.*; import org.apache.struts.action.*;

public class WebmailForm extends FileUploadForm{

private String _recipients; private String _subject; private String _message;

public String getRecipients(){ return _recipients;

}

public String getSubject(){ return _subject;

}

public String getMessage(){ return _message;

}

public void setRecipients(String recipients){ _recipients = recipients;

}

public void setSubject(String subject){ _subject = subject;

}

136

C H A P T E R 1 1 U P L O A D I N G F I L E S

public void setMessage(String message){ _message = message;

}

public ActionErrors validate(ActionMapping mapping, HttpServletRequest request){

ActionErrors errors = new ActionErrors();

//NO BLANK MESSAGES/RECIPIENTS/SUBJECTS if(_recipients.trim().length() == 0){

errors.add("recipients",

new ActionMessage("webmail.error.recipients"));

}

if(_subject.trim().length() == 0){ errors.add("subject",

new ActionMessage("webmail.error.subject"));

}

if(_message.trim().length() == 0){ errors.add("message",

new ActionMessage("webmail.error.message"));

}

return errors;

}

}

Listing 11-6 contains the JSP page that displays the compose page shown in Figure 11-2. For clarity, and to highlight the use of message keys in conjunction with LookupDispatchAction, I’ve not used message resources consistently in Listing 11-6.

Listing 11-6. JSP for the Compose Page

<%@ taglib uri="/tags/struts-html" prefix="html" %> <%@ taglib uri="/tags/struts-bean" prefix="bean" %> <html>

<body>

<html:form enctype="multipart/form-data" action="/SendMessage.do">

Recipients: <html:text property="recipients"/> <html:errors property="recipients"/><br>

C H A P T E R 1 1 U P L O A D I N G F I L E S

137

Subject: <html:text property="subject"/> <html:errors property="subject"/><br>

Message: <html:text property="message"/> <html:errors property="message"/><br>

<h1>Attach Files</h1>

File 1:<html:file property="file[0]" size="20"/><br> File 2:<html:file property="file[1]" size="20"/><br> File 3:<html:file property="file[2]" size="20"/><br> File 4:<html:file property="file[3]" size="20"/>

<html:submit property="action">

<bean:message key="webmail.prompt.more-files"/> </html:submit><br>

<html:submit property="action"> <bean:message key="webmail.prompt.send"/>

</html:submit>

</html:form>

</body>

</html>

In Listing 11-6, note the two submit buttons, as well as the required setup for using LookupDispatchAction (see Chapter 17). The Action subclass to handle both submits appears in Listing 11-7. Notice that we do not override execute() in SendMessageAction because of how LookupDispatchAction works.

Listing 11-7. SendMessageAction

package net.thinksquared.webmail;

import java.util.*;

import javax.servlet.http.*; import org.apache.struts.action.*;

import org.apache.struts.actions.*;

public class SendMessageAction extends LookupDispatchAction{