001: /*
002: * Copyright 2000,2005 wingS development team.
003: *
004: * This file is part of wingS (http://wingsframework.org).
005: *
006: * wingS is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU Lesser General Public License
008: * as published by the Free Software Foundation; either version 2.1
009: * of the License, or (at your option) any later version.
010: *
011: * Please see COPYING for the complete licence.
012: */
013: package org.wings.session;
014:
015: import org.apache.commons.logging.Log;
016: import org.apache.commons.logging.LogFactory;
017: import org.wings.UploadFilterManager;
018: import org.wings.util.SStringBuilder;
019: import org.wings.util.LocaleCharSet;
020:
021: import javax.servlet.ServletInputStream;
022: import javax.servlet.http.HttpServletRequest;
023: import javax.servlet.http.HttpServletRequestWrapper;
024: import java.io.*;
025: import java.net.URLEncoder;
026: import java.util.*;
027:
028: /**
029: * A utility class to handle <tt>multipart/form-data</tt> requests,
030: * the kind of requests that support file uploads. This class can
031: * receive arbitrarily large files (up to an artificial limit you can set),
032: * and fairly efficiently too. And it knows and works around several browser
033: * bugs that don't know how to upload files correctly.
034: * <p/>
035: * A client can upload files using an HTML form with the following structure.
036: * Note that not all browsers support file uploads.
037: * <blockquote><pre>
038: * <FORM ACTION="/servlet/Handler" METHOD=POST
039: * ENCTYPE="multipart/form-data">
040: * What is your name? <INPUT TYPE=TEXT NAME=submitter> <BR>
041: * Which file to upload? <INPUT TYPE=FILE NAME=file> <BR>
042: * <INPUT TYPE=SUBMIT>
043: * </FORM>
044: * </pre></blockquote>
045: * <p/>
046: * The full file upload specification is contained in experimental RFC 1867,
047: * available at <a href="http://ds.internic.net/rfc/rfc1867.txt">
048: * http://ds.internic.net/rfc/rfc1867.txt</a>.
049: *
050: * @author Holger Engels
051: */
052: public final class MultipartRequest extends HttpServletRequestWrapper {
053: private final transient static Log log = LogFactory
054: .getLog(MultipartRequest.class);
055:
056: private static final int DEFAULT_MAX_POST_SIZE = 1024 * 1024; // 1 Meg
057:
058: private int maxSize;
059: private boolean urlencodedRequest;
060:
061: private Map<String, List> parameters; // name - value
062: private Map<String, UploadedFile> files; // name - UploadedFile
063: private Map<String, String[]> parameterMap; // name - values
064:
065: /**
066: * @param request the servlet request
067: * @throws IOException if the uploaded content is larger than 1 Megabyte
068: * or there's a problem reading or parsing the request
069: */
070: public MultipartRequest(HttpServletRequest request)
071: throws IOException {
072: this (request, DEFAULT_MAX_POST_SIZE);
073: }
074:
075: /**
076: * @param request the servlet request
077: * @param maxPostSize the maximum size of the POST content
078: * @throws IOException if the uploaded content is larger than
079: * <tt>maxPostSize</tt> or there's a problem reading or parsing the request
080: */
081: public MultipartRequest(HttpServletRequest request, int maxPostSize)
082: throws IOException {
083: super (request);
084:
085: if (request == null)
086: throw new IllegalArgumentException("request cannot be null");
087: if (maxPostSize <= 0)
088: throw new IllegalArgumentException(
089: "maxPostSize must be positive");
090:
091: maxSize = maxPostSize;
092:
093: processRequest(request);
094: }
095:
096: /**
097: * Returns the names of all the parameters as an Enumeration of
098: * Strings. It returns an empty Enumeration if there are no parameters.
099: *
100: * @return the names of all the parameters as an Enumeration of Strings
101: */
102: public Enumeration getParameterNames() {
103: if (urlencodedRequest)
104: return super .getParameterNames();
105:
106: final Iterator iter = parameters.keySet().iterator();
107: return new Enumeration() {
108: public boolean hasMoreElements() {
109: return iter.hasNext();
110: }
111:
112: public Object nextElement() {
113: return iter.next();
114: }
115: };
116: }
117:
118: /**
119: * Returns the names of all the uploaded files as an Enumeration of
120: * Strings. It returns an empty Enumeration if there are no uploaded
121: * files. Each file name is the name specified by the form, not by
122: * the user.
123: *
124: * @return the names of all the uploaded files as an Enumeration of Strings
125: */
126: public Iterator getFileNames() {
127: if (urlencodedRequest)
128: return Collections.EMPTY_SET.iterator();
129: return files.keySet().iterator();
130: }
131:
132: public String[] getParameterValues(String name) {
133: if (urlencodedRequest)
134: return super .getParameterValues(name);
135:
136: List v = parameters.get(name);
137: if (v == null)
138: return null;
139: String result[] = new String[v.size()];
140: return (String[]) v.toArray(result);
141: }
142:
143: public Map getParameterMap() {
144: if (urlencodedRequest)
145: return super .getParameterMap();
146:
147: if (parameterMap == null) {
148: parameterMap = new HashMap<String, String[]>();
149: Iterator<Map.Entry<String, List>> it = parameters
150: .entrySet().iterator();
151: while (it.hasNext()) {
152: Map.Entry<String, List> entry = it.next();
153: List list = entry.getValue();
154: String[] values = (String[]) list
155: .toArray(new String[list.size()]);
156: parameterMap.put(entry.getKey(), values);
157: }
158: }
159: return parameterMap;
160: }
161:
162: /**
163: * Returns the filename of the specified file, or null if the
164: * file was not included in the upload. The filename is the name
165: * specified by the user. It is not the name under which the file is
166: * actually saved.
167: *
168: * @param name the file name
169: * @return the filesystem name of the file
170: */
171: public String getFileName(String name) {
172: try {
173: return files.get(name).getFileName();
174: } catch (Exception e) {
175: return null;
176: }
177: }
178:
179: /**
180: * Returns the fileid of the specified file, or null if the
181: * file was not included in the upload. The fileid is the name
182: * under which the file is saved in the filesystem.
183: *
184: * @param name the file name
185: * @return the filesystem name of the file
186: */
187: public String getFileId(String name) {
188: try {
189: return files.get(name).getId();
190: } catch (Exception e) {
191: return null;
192: }
193: }
194:
195: /**
196: * Returns the content type of the specified file (as supplied by the
197: * client browser), or null if the file was not included in the upload.
198: *
199: * @param name the file name
200: * @return the content type of the file
201: */
202: public String getContentType(String name) {
203: try {
204: return files.get(name).getContentType();
205: } catch (Exception e) {
206: return null;
207: }
208: }
209:
210: /**
211: * Returns a File object for the specified file saved on the server's
212: * filesystem, or null if the file was not included in the upload.
213: *
214: * @param name the file name
215: * @return a File object for the named file
216: */
217: public File getFile(String name) {
218: try {
219: return files.get(name).getFile();
220: } catch (Exception e) {
221: return null;
222: }
223: }
224:
225: /**
226: * Indicates if this class was successfully able to parse request as multipart request.
227: */
228: public final boolean isMultipart() {
229: return !urlencodedRequest;
230: }
231:
232: /**
233: * Store exception as request parameter.
234: */
235: protected void setException(String param, Exception ex) {
236: if (!urlencodedRequest) {
237: // I'm not 100% sure if it's ok to comment out the following line!
238: // However, if we delete all parameters that have been set before
239: // the occurence of the exception, the only component that will get
240: // triggered during event processing is the filechooser. But since
241: // a filechooser provides no ability to register any listeners, the
242: // developer has no chance to get informed about the exception in
243: // the application code. There is only one reason I can imaging why
244: // someone set this line: if other components have been placed below
245: // the filechooser on the GUI, their parts won't get processed by
246: // the MultipartRequest, the according parameters won't be set and
247: // therefore no event processing of such components is done. If we
248: // process components above the exception raising filechooser but
249: // not components below it, we might end up in an inconsitent state.
250: // Anyway, I think it's the better solution to leave it out here!!!
251: //
252: // -- stephan
253: //
254: // parameters.clear();
255: files.clear();
256: }
257: putParameter(param, "exception");
258: putParameter(param, ex.getMessage());
259: }
260:
261: /**
262: * Parses passed request and stores contained parameters.
263: *
264: * @throws IOException On unrecoverable parsing bugs due to old Tomcat version.
265: */
266: protected void processRequest(HttpServletRequest req)
267: throws IOException {
268: String type = req.getContentType();
269: if (type == null
270: || !type.toLowerCase()
271: .startsWith("multipart/form-data")) {
272: urlencodedRequest = true;
273: return;
274: }
275:
276: urlencodedRequest = false;
277: parameters = new HashMap<String, List>();
278: files = new HashMap<String, UploadedFile>();
279:
280: for (Iterator iterator = req.getParameterMap().entrySet()
281: .iterator(); iterator.hasNext(); /**/) {
282: Map.Entry entry = (Map.Entry) iterator.next();
283: parameters.put((String) entry.getKey(), new ArrayList(
284: Arrays.asList((String[]) entry.getValue())));
285: }
286:
287: String boundaryToken = extractBoundaryToken(type);
288: if (boundaryToken == null) {
289: /*
290: * this could happen due to a bug in Tomcat 3.2.2 in combination
291: * with Opera.
292: * Opera sends the boundary on a separate line, which is perfectly
293: * correct regarding the way header may be constructed
294: * (multiline headers). Alas, Tomcat fails to read the header in
295: * the content type line and thus we cannot read it.. haven't
296: * checked later versions of Tomcat, but upgrading is
297: * definitly needed, since we cannot do anything about it here.
298: * (JServ works fine, BTW.) (Henner)
299: */
300: throw new IOException(
301: "Separation boundary was not specified (BUG in Tomcat 3.* with Opera?)");
302: }
303:
304: MultipartInputStream mimeStream = null;
305:
306: ByteArrayOutputStream headerByteArray;
307: SStringBuilder content = new SStringBuilder();
308: HashMap headers = null;
309: int currentByte = 0;
310: int currentPos = 0;
311: int currentTransformByte = 0;
312: String currentParam = null;
313: File uploadFile = null;
314: OutputStream fileStream = null;
315: boolean done;
316: int last = -1;
317:
318: try {
319: mimeStream = new MultipartInputStream(req.getInputStream(),
320: req.getContentLength(), maxSize);
321: while (currentByte != -1) {
322: // Read MIME part header line
323: done = false;
324: headerByteArray = new ByteArrayOutputStream();
325: while ((currentByte = mimeStream.read()) != -1 && !done) {
326: headerByteArray.write(currentByte);
327: done = (last == '\n' && currentByte == '\r');
328: last = currentByte;
329: }
330: if (currentByte == -1)
331: break;
332:
333: headers = parseHeader(headerByteArray.toString(req
334: .getCharacterEncoding()));
335: headerByteArray.reset();
336: currentParam = (String) headers.get("name");
337:
338: if (headers.size() == 1) { // .. it's not a file
339: byte[] bytes = new byte[req.getContentLength()];
340: currentPos = 0;
341: while ((currentByte = mimeStream.read()) != -1) {
342: bytes[currentPos] = (byte) currentByte;
343: currentPos++;
344: if (currentPos >= boundaryToken.length()) {
345: int i;
346: for (i = 0; i < boundaryToken.length(); i++) {
347: if (boundaryToken.charAt(boundaryToken
348: .length()
349: - i - 1) != bytes[currentPos
350: - i - 1]) {
351: i = 0;
352: break;
353: }
354: }
355: if (i == boundaryToken.length()) { // end of part ..
356: ByteArrayInputStream bais = new ByteArrayInputStream(
357: bytes, 0, currentPos
358: - boundaryToken
359: .length() - 4);
360: InputStreamReader ir;
361: if (req.getCharacterEncoding() != null)
362: // It's common behaviour of browsers to encode their form input in the character
363: // encoding of the page, though they don't declare the used characterset explicetly
364: // for backward compatibility.
365: ir = new InputStreamReader(bais,
366: req.getCharacterEncoding());
367: else
368: ir = new InputStreamReader(bais);
369: content.setLength(0);
370: while ((currentTransformByte = ir
371: .read()) != -1) {
372: content
373: .append((char) currentTransformByte);
374: }
375:
376: putParameter(currentParam, content
377: .toString());
378: break;
379: }
380: }
381: }
382: } else { // .. it's a file
383: String filename = (String) headers.get("filename");
384: if (filename != null && filename.length() != 0) {
385: // The filename may contain a full path. Cut to just the filename.
386: int slash = Math.max(filename.lastIndexOf('/'),
387: filename.lastIndexOf('\\'));
388: if (slash > -1) {
389: filename = filename.substring(slash + 1);
390: }
391: String name = (String) headers.get("name");
392:
393: String contentType = (String) headers
394: .get("content-type");
395: try {
396: uploadFile = File.createTempFile(
397: "wings_uploaded", "tmp");
398: } catch (IOException e) {
399: log
400: .error(
401: "couldn't create temp file in '"
402: + System
403: .getProperty("java.io.tmpdir")
404: + "' (CATALINA_TMPDIR set correctly?)",
405: e);
406: throw e;
407: }
408:
409: UploadedFile upload = new UploadedFile(
410: filename, contentType, uploadFile);
411: fileStream = new FileOutputStream(uploadFile);
412:
413: fileStream = UploadFilterManager
414: .createFilterInstance(name, fileStream);
415:
416: AccessibleByteArrayOutputStream byteArray = new AccessibleByteArrayOutputStream();
417: byte[] bytes = null;
418:
419: int blength = boundaryToken.length();
420: int i;
421: while ((currentByte = mimeStream.read()) != -1) {
422: byteArray.write(currentByte);
423: for (i = 0; i < blength; i++) {
424: if (boundaryToken.charAt(blength - i
425: - 1) != byteArray
426: .charAt(-i - 1)) {
427: i = 0;
428: if (byteArray.size() > 512 + blength + 2)
429: byteArray.writeTo(fileStream,
430: 512);
431: break;
432: }
433: }
434: if (i == blength) // end of part ..
435: break;
436: }
437: bytes = byteArray.toByteArray();
438: fileStream.write(bytes, 0, bytes.length
439: - blength - 4);
440: fileStream.close();
441:
442: files.put(name, upload);
443: putParameter(name, upload.toString());
444: } else { // workaround for some netscape bug
445: int i;
446: int blength = boundaryToken.length();
447: while ((currentByte = mimeStream.read()) != -1) {
448: content.append((char) currentByte);
449: if (content.length() >= blength) {
450: for (i = 0; i < blength; i++) {
451: if (boundaryToken.charAt(blength
452: - i - 1) != content
453: .charAt(content.length()
454: - i - 1)) {
455: i = 0;
456: break;
457: }
458: }
459: if (i == blength)
460: break;
461: }
462: }
463: }
464: }
465:
466: currentByte = mimeStream.read();
467: if (currentByte == '\r' && mimeStream.read() != '\n')
468: log.error("No line return char? " + currentByte);
469: if (currentByte == '-' && mimeStream.read() != '-')
470: log.error("?? No clue " + currentByte);
471: }
472: } catch (IOException ex) {
473: // cleanup and store the exception for notification of SFileChooser
474: log.warn("upload", ex);
475: if (uploadFile != null)
476: uploadFile.delete();
477: setException(currentParam, ex);
478: } finally {
479: try {
480: fileStream.close();
481: } catch (Exception ign) {
482: }
483: try {
484: mimeStream.close();
485: } catch (Exception ign) {
486: }
487: }
488: }
489:
490: private static class AccessibleByteArrayOutputStream extends
491: ByteArrayOutputStream {
492: public byte charAt(int index) {
493: if (count + index < 0) {
494: log.warn("count: " + count + ", index: " + index
495: + ", buffer: " + new String(buf));
496: return -1;
497: }
498: if (index < 0)
499: return buf[count + index];
500: if (index < count)
501: return buf[index];
502: return -1;
503: }
504:
505: public byte[] getBuffer() {
506: return buf;
507: }
508:
509: public void writeTo(OutputStream out, int num)
510: throws IOException {
511: out.write(buf, 0, num);
512: System.arraycopy(buf, num, buf, 0, count - num);
513: count = count - num;
514: }
515: }
516:
517: private HashMap parseHeader(String header) {
518: int lastHeader = -1;
519: String[] headerLines;
520: HashMap nameValuePairs = new HashMap();
521:
522: StringTokenizer stLines = new StringTokenizer(header, "\r\n",
523: false);
524: headerLines = new String[stLines.countTokens()];
525:
526: // Get all the header lines
527: while (stLines.hasMoreTokens()) {
528: String hLine = stLines.nextToken();
529: if (hLine.length() == 0)
530: continue;
531: /* if the first character is a space, then
532: * this line is a header continuation.
533: * (opera sends multiline headers..)
534: */
535: if (lastHeader >= 0
536: && Character.isWhitespace(hLine.charAt(0)))
537: headerLines[lastHeader] += hLine;
538: else
539: headerLines[++lastHeader] = hLine;
540: }
541:
542: for (int i = 0; i <= lastHeader; ++i) {
543: String currentHeader = headerLines[i];
544: if (currentHeader.startsWith("Content-Type")) {
545: String contentType = currentHeader
546: .substring(currentHeader.indexOf(':') + 1);
547: int semiColonPos = contentType.indexOf(';');
548: if (semiColonPos != -1)
549: contentType = contentType
550: .substring(0, semiColonPos);
551: nameValuePairs.put("content-type", contentType.trim());
552: continue;
553: }
554:
555: if (!currentHeader.startsWith("Content-Disposition"))
556: continue;
557:
558: StringTokenizer stTokens = new StringTokenizer(
559: currentHeader, ";", false);
560:
561: // Get all the tokens from each line
562: if (stTokens.countTokens() > 1) {
563: stTokens.nextToken(); // Skip fist Token Content-Disposition: form-data
564: StringTokenizer stnameValue = new StringTokenizer(
565: stTokens.nextToken(), "=", false);
566: nameValuePairs.put(stnameValue.nextToken().trim(),
567: trim(stnameValue.nextToken(), "\""));
568:
569: // This is a file
570: if (stTokens.hasMoreTokens()) {
571: stnameValue = new StringTokenizer(stTokens
572: .nextToken(), "=", false);
573:
574: String formType = stnameValue.nextToken().trim(); // String Object default function
575: String filePath = trim(stnameValue.nextToken(),
576: "\""); // Our own trim function.
577: // If is a DOS file get rid of drive letter and colon "e:"
578: if (filePath.indexOf(":") != -1)
579: filePath = filePath.substring((filePath
580: .indexOf(":") + 1));
581:
582: // get rid of PATH
583: filePath = filePath.substring(filePath
584: .lastIndexOf(File.separator) + 1);
585: nameValuePairs.put(formType, filePath);
586: }
587: }
588: }
589: return nameValuePairs;
590: }
591:
592: /**
593: * This method gets the substring enclosed in trimChar ; "string" returns string
594: */
595: private String trim(String source, String trimChar) {
596: String target = "";
597: //Blank space from both sides
598: source = source.trim();
599:
600: // Make sure a substring is enclosed between specified characters
601: if (source.indexOf(trimChar) != -1
602: && (source.lastIndexOf(trimChar) >= (source
603: .indexOf(trimChar) + 1)))
604: // Remove double character from both sides
605: target = source.substring(source.indexOf(trimChar) + 1,
606: source.lastIndexOf(trimChar));
607:
608: return target;
609: }
610:
611: private static class MultipartInputStream extends InputStream {
612: ServletInputStream istream = null;
613: int len, pos, maxLength;
614:
615: public MultipartInputStream(ServletInputStream istream,
616: int len, int maxLength) {
617: this .istream = istream;
618: this .len = len;
619: this .pos = 0;
620: this .maxLength = maxLength;
621: }
622:
623: /**
624: * @return bytes available in stream.
625: */
626: public int available() throws IOException {
627: return len - pos - 1;
628: }
629:
630: /**
631: * @return Next byte in Request.
632: * @throws IOException
633: */
634: public int read() throws IOException {
635: if (pos >= maxLength)
636: throw new IOException("Size (" + len
637: + ") exceeds maxlength " + maxLength);
638:
639: if (pos >= len)
640: return -1;
641: pos++;
642:
643: return istream.read();
644: }
645:
646: public int read(byte b[]) throws IOException {
647: return read(b, 0, b.length);
648: }
649:
650: public int read(byte b[], int off, int num) throws IOException {
651: if (off > 0)
652: istream.skip(off);
653:
654: if (pos >= len)
655: return -1;
656:
657: if (num > len - pos)
658: num = len - pos;
659:
660: num = istream.read(b, 0, num);
661: pos += num;
662:
663: if (pos >= maxLength)
664: throw new IOException("Size (" + len
665: + ") exceeds maxlength " + maxLength);
666:
667: return num;
668: }
669:
670: public long skip(long num) throws IOException {
671: if (pos >= len)
672: return -1;
673:
674: if (num > len - pos)
675: num = len - pos;
676:
677: num = istream.skip(num);
678: pos += num;
679:
680: if (pos >= maxLength)
681: throw new IOException("Size (" + len
682: + ") exceeds maxlength " + maxLength);
683:
684: return num;
685: }
686:
687: public void close() throws IOException {
688: //Ignore closing of the input stream ..
689: }
690: }
691:
692: /**
693: * Stores a parameter identified in this request.
694: */
695: protected void putParameter(String name, String value) {
696: ArrayList v = (ArrayList) parameters.get(name);
697: // there is no Parameter yet; create one
698: if (v == null) {
699: v = new ArrayList(2);
700: parameters.put(name, v);
701: }
702: v.add(value);
703: }
704:
705: /**
706: * Extracts and returns the boundary token from a line.
707: */
708: private String extractBoundaryToken(String line) {
709: int index = line.indexOf("boundary=");
710: if (index == -1) {
711: return null;
712: }
713: String boundary = line.substring(index + 9); // 9 for "boundary="
714: // The real boundary is always preceeded by an extra "--"
715: //boundary = "--" + boundary;
716:
717: return boundary;
718: }
719:
720: /**
721: * Extracts and returns the content type from a line, or null if the line was empty.
722: *
723: * @throws IOException if the line is malformatted.
724: */
725: private String extractContentType(String line) throws IOException {
726: String contentType = null;
727:
728: // Convert the line to a lowercase string
729: String origline = line;
730: line = origline.toLowerCase();
731:
732: // Get the content type, if any
733: if (line.startsWith("content-type")) {
734: int start = line.indexOf(" ");
735: if (start == -1) {
736: throw new IOException("Content type corrupt: "
737: + origline);
738: }
739: contentType = line.substring(start + 1);
740: } else if (line.length() != 0) { // no content type, so should be empty
741: throw new IOException("Malformed line after disposition: "
742: + origline);
743: }
744:
745: return contentType;
746: }
747:
748: private static long uniqueId = 0;
749:
750: private static final synchronized String uniqueId() {
751: uniqueId++;
752: return System.currentTimeMillis() + "." + uniqueId;
753: }
754:
755: /**
756: * A class to hold information about an uploaded file.
757: */
758: class UploadedFile {
759: private String fileName;
760: private String type;
761: private File uploadedFile;
762:
763: UploadedFile(String fileName, String type, File f) {
764: this .uploadedFile = f;
765: this .fileName = fileName;
766: this .type = type;
767: }
768:
769: /**
770: * @return Path of uploaded file
771: */
772: public String getDir() {
773: if (uploadedFile != null)
774: return uploadedFile.getParentFile().getPath();
775: else
776: return null;
777: }
778:
779: /**
780: * @return Filename passed by browser
781: */
782: public String getFileName() {
783: return fileName;
784: }
785:
786: /**
787: * @return MIME type passed by browser
788: */
789: public String getContentType() {
790: return type;
791: }
792:
793: /**
794: * @return Uploaded file
795: */
796: public File getFile() {
797: return uploadedFile;
798: }
799:
800: /**
801: * @return Uploaded file name
802: */
803: public String getId() {
804: if (uploadedFile != null)
805: return uploadedFile.getName();
806: else
807: return null;
808: }
809:
810: /**
811: * create a URL-encoded form of this uploaded file, that contains
812: * all parameters important for this file. The parameters returned
813: * are 'dir', 'name', 'type' and 'id'
814: * <ul>
815: * <li>'dir' contains the directory in the filesystem, the file
816: * has been stored into.</li>
817: * <li>'name' contains the filename as provided by the user</li>
818: * <li>'type' contains the mime-type of this file.</li>
819: * <li>'id' contains the internal name of the file in the
820: * filesystem.</li>
821: * </ul>
822: */
823: public String toString() {
824: String encoding = getRequest().getCharacterEncoding() != null ? getRequest()
825: .getCharacterEncoding()
826: : LocaleCharSet.DEFAULT_ENCODING;
827:
828: try {
829: SStringBuilder buffer = new SStringBuilder();
830: buffer.append("dir=");
831: buffer.append(URLEncoder.encode(getDir(), encoding));
832: if (getFileName() != null) {
833: buffer.append("&name=");
834: buffer.append(URLEncoder.encode(getFileName(),
835: encoding));
836: }
837: if (getContentType() != null) {
838: buffer.append("&type=");
839: buffer.append(URLEncoder.encode(getContentType(),
840: encoding));
841: }
842: buffer.append("&id=");
843: buffer.append(URLEncoder.encode(getId(), encoding));
844:
845: return buffer.toString();
846: } catch (UnsupportedEncodingException e) {
847: log.error(getClass().getName(), e);
848: return null;
849: }
850: }
851: }
852: }
|