From fd563874f323ac66506f57f5fea7a2228a33957f Mon Sep 17 00:00:00 2001 From: Imran M Yousuf Date: Mon, 23 Mar 2009 13:25:45 +0600 Subject: [PATCH] Add convenience form body part extension for java.io.File The convenience body part will predict the MIME Type of the file and will assign it appropriately to the body part, it will also add information to the form data content disposition, e.g. it will add the name of the param, file name, file size and last date of modification of the file to the content disposition automatically. It will also update the content disposition upon update name or file entity or media type automatically. The predictable MIME types are declared in a enum thus just updating that can expand the predictable MIME type list. The test case for FileDataBodyPart is similar to FormDataBodyPart and it modifies accordingly. Signed-off-by: Imran M Yousuf --- .../com/sun/jersey/multipart/CommonMediaType.java | 104 ++++++++ .../com/sun/jersey/multipart/FileDataBodyPart.java | 266 ++++++++++++++++++++ .../sun/jersey/multipart/FileDataBodyPartTest.java | 144 +++++++++++ 3 files changed, 514 insertions(+), 0 deletions(-) create mode 100644 contribs/jersey-multipart/src/main/java/com/sun/jersey/multipart/CommonMediaType.java create mode 100644 contribs/jersey-multipart/src/main/java/com/sun/jersey/multipart/FileDataBodyPart.java create mode 100644 contribs/jersey-multipart/src/test/java/com/sun/jersey/multipart/FileDataBodyPartTest.java diff --git a/contribs/jersey-multipart/src/main/java/com/sun/jersey/multipart/CommonMediaType.java b/contribs/jersey-multipart/src/main/java/com/sun/jersey/multipart/CommonMediaType.java new file mode 100644 index 0000000..426f4d3 --- /dev/null +++ b/contribs/jersey-multipart/src/main/java/com/sun/jersey/multipart/CommonMediaType.java @@ -0,0 +1,104 @@ +/* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can obtain + * a copy of the License at https://jersey.dev.java.net/CDDL+GPL.html + * or jersey/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at jersey/legal/LICENSE.txt. + * Sun designates this particular file as subject to the "Classpath" exception + * as provided by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the License + * Header, with the fields enclosed by brackets [] replaced by your own + * identifying information: "Portions Copyrighted [year] + * [name of copyright owner]" + * + * Contributor(s): + * + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package com.sun.jersey.multipart; + +import javax.ws.rs.core.MediaType; + +/** + * This enum represents file extension and MIME types of commonly used file. It + * is to be noted that all file extension and MIME types are specified in lower + * case, when checking the extension this should be kept in mind. + * Curently supported file extension and MIME Types are - + *
+ */ +public enum CommonMediaType { + + XML(".xml", MediaType.APPLICATION_XML_TYPE), + TXT(".txt", MediaType.TEXT_PLAIN_TYPE), + HTM(".htm", MediaType.TEXT_HTML_TYPE), + HTML(".html", MediaType.TEXT_HTML_TYPE), + PDF(".pdf", new MediaType("application", "pdf")), + JPG(".jpg", new MediaType("image", "jpeg")), + PNG(".png", new MediaType("image", "png")), + GIF(".gif", new MediaType("image", "gif")), + BMP(".bmp", new MediaType("image", "pdf")), + TAR(".tar", new MediaType("application", "x-tar")), + ZIP(".zip", new MediaType("application", "zip")), + GZ(".gz", new MediaType("application", "x-gzip")), + RAR(".rar", new MediaType("application", "x-rar")), + MP3(".mp3", new MediaType("audio", "mpeg")), + WAV(".wav", new MediaType("audio", "x-wave")), + AVI(".avi", new MediaType("video", "x-msvideo")), + MPEG(".mpeg", new MediaType("video", "mpeg")),; + private final String extension; + private final MediaType mediaType; + + private CommonMediaType(final String extension, + final MediaType mediaType) { + if (extension == null || !extension.startsWith(".") || mediaType == null) { + throw new IllegalArgumentException(); + } + this.extension = extension; + this.mediaType = mediaType; + } + + public String getExtension() { + return extension; + } + + public MediaType getMediaType() { + return mediaType; + } +} diff --git a/contribs/jersey-multipart/src/main/java/com/sun/jersey/multipart/FileDataBodyPart.java b/contribs/jersey-multipart/src/main/java/com/sun/jersey/multipart/FileDataBodyPart.java new file mode 100644 index 0000000..3295bba --- /dev/null +++ b/contribs/jersey-multipart/src/main/java/com/sun/jersey/multipart/FileDataBodyPart.java @@ -0,0 +1,266 @@ +/* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can obtain + * a copy of the License at https://jersey.dev.java.net/CDDL+GPL.html + * or jersey/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at jersey/legal/LICENSE.txt. + * Sun designates this particular file as subject to the "Classpath" exception + * as provided by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the License + * Header, with the fields enclosed by brackets [] replaced by your own + * identifying information: "Portions Copyrighted [year] + * [name of copyright owner]" + * + * Contributor(s): + * + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package com.sun.jersey.multipart; + +import com.sun.jersey.core.header.FormDataContentDisposition; +import java.io.File; +import java.text.ParseException; +import java.util.Date; +import javax.ws.rs.core.MediaType; + +/** + * This is a convenient extension of {@link FormDataBodyPart} for associating + * File as a body part; an example would be file attachment. It will add the + * Content-Disposition and Content-Type header in the body part for you from the + * attributes set to it and will update the headers as you set their value in + * the course of the objects life cycle. + */ +public class FileDataBodyPart + extends FormDataBodyPart { + + private File fileEntity; + + /** + * A no-args constructor which expects its client to set the values + * individually, the attributes to be set are fileEntity and name; the + * media type will be predicted from the fileEntity if not set explicitly + */ + public FileDataBodyPart() { + super(); + } + + /** + * Constructs the body part with the provided name and file, it predicts the + * {@link MediaType} of the file provided. For the known media types client + * will not need to set the media type explicitly. + * @param name The name of body part + * @param fileEntity The file that represents the entity + * @see FileDataBodyPart#getMediaTypeFromFile(java.io.File) + * @see FileDataBodyPart#FileDataBodyPart(java.lang.String, java.io.File, javax.ws.rs.core.MediaType) + */ + public FileDataBodyPart(final String name, + final File fileEntity) { + this(name, fileEntity, getMediaTypeFromFile(fileEntity)); + } + + /** + * Constructs the body part with the provided name and file, it predicts the + * {@link MediaType} of the file provided. For the known media types client + * will not need to set the media type explicitly. + * @param fileEntity The file that represents the entity + * @see FileDataBodyPart#getMediaTypeFromFile(java.io.File) + * @see FileDataBodyPart#FileDataBodyPart(java.lang.String, java.io.File, javax.ws.rs.core.MediaType) + */ + public FileDataBodyPart(final File fileEntity) { + this(null, fileEntity, getMediaTypeFromFile(fileEntity)); + } + + /** + * Constructs the body part with all the attributes set for its proper + * function. If this constructor is used to construct the body part then it + * is not required to set any other attributes for proper behavior. + * @param name The name of body part + * @param fileEntity The file that represents the entity + * @param mediaType The {@link MediaType} of the body part + * @throws java.lang.IllegalArgumentException If the fileEntity is null + */ + public FileDataBodyPart(final String name, + final File fileEntity, + final MediaType mediaType) + throws IllegalArgumentException { + super(); + super.setName(name); + setFileEntity(mediaType, fileEntity); + } + + /** + * A utility method for predicting media type from file name. + * @param file The file for which to predict the {@link MediaType} + * @return The probably {@link MediaType} for the give file; NULL - if file + * is null; "*\/*" if extension not recognized. + * @see FileDataBodyPart#getMediaTypeFromFileName(java.lang.String) + */ + public static MediaType getMediaTypeFromFile(final File file) { + if (file == null) { + return null; + } + String fileName = file.getName(); + return getMediaTypeFromFileName(fileName); + } + + /** + * A utility method for predicting media type from file name. If the file + * name extension is not recognised it will return {@link MediaType} for + * "*\/*", it will also return the same if the file is NULL. Currently + * supported file extensions can be found at {@link CommonMediaType}. + * @param fileName The file name for which to predict the {@link MediaType} + * @return The probably {@link MediaType} for the give file; NULL - if file + * is null; "*\/*" if extension not recognized. + */ + public static MediaType getMediaTypeFromFileName(String fileName) { + if (fileName == null) { + return null; + } + CommonMediaType[] types = CommonMediaType.values(); + if (types != null && types.length > 0) { + for (CommonMediaType type : types) { + if (fileName != null && + fileName.toLowerCase().endsWith(type.getExtension())) { + return type.getMediaType(); + } + } + } + return MediaType.WILDCARD_TYPE; + } + + /** + * Get the file for this body part. + * @return File entity for this body part + */ + public File getFileEntity() { + return fileEntity; + } + + @Override + public void setName(String name) { + super.setName(name); + setHeaders(); + } + + @Override + public void setMediaType(MediaType mediaType) { + super.setMediaType(mediaType); + setHeaders(); + } + + /** + * This operation is not supported from this implementation. + * @param mediaType + * @param value + * @throws java.lang.UnsupportedOperationException Operation not supported. + * @see FileDataBodyPart#setFileEntity(javax.ws.rs.core.MediaType, java.io.File) + */ + @Override + public void setValue(MediaType mediaType, + Object value) + throws UnsupportedOperationException { + throw new UnsupportedOperationException("It is unsupported, please " + + "use setFileEntity instead!"); + } + + /** + * This operation is not supported from this implementation. + * @param entity + * @throws java.lang.UnsupportedOperationException Operation not supported. + * @see FileDataBodyPart#setFileEntity(java.io.File) + */ + @Override + public void setEntity(Object entity) + throws UnsupportedOperationException { + throw new UnsupportedOperationException("It is unsupported, please " + + "use setFileEntity instead!"); + } + + /** + * Sets the {@link MediaType} and fileEntity for this {@link FormDataBodyPart}. + * @param fileEntity The entity of this body part + */ + public void setFileEntity(final MediaType mediaType, + final File fileEntity) { + super.setMediaType(mediaType); + setFileEntity(fileEntity); + } + + /** + * Sets the fileEntity for this {@link FormDataBodyPart}. + * @param fileEntity The entity of this {@link FormDataBodyPart}. + */ + public void setFileEntity(final File fileEntity) + throws IllegalArgumentException { + this.fileEntity = fileEntity; + super.setEntity(this.fileEntity); + setHeaders(); + } + + /** + * Sets the relevant Content-* headers in the {@link FormDataBodyPart} for + * the given file if and only if the fileEntity is not null and name of the + * body part isn't blank; else it will not set the headers. + */ + protected void setHeaders() { + if (getFileEntity() == null || getName() == null || + getName().length() <= 0) { + return; + } + if (getMediaType() == null) { + super.setMediaType(getMediaTypeFromFile(getFileEntity())); + } + getHeaders().putSingle("Content-Type", getMediaType().toString()); + try { + setFormDataContentDisposition(getContentDisposition(getName(), + getFileEntity())); + } + catch (ParseException ex) { + throw new IllegalArgumentException(ex); + } + + } + + /** + * It builds the content-disposition headers value from the parameter name + * and file used by this body part. + * @param paramName The parameter name for the body part, if blank will be + * ignored. + * @param file The file used by the body part, may be null. + * @return The content-disposition header. + */ + protected FormDataContentDisposition getContentDisposition( + final String paramName, + final File file) + throws ParseException { + FormDataContentDisposition.FormDataContentDispositionBuilder builder = + FormDataContentDisposition.name(paramName); + if (file != null) { + builder.fileName(file.getName()); + if (file.exists()) { + builder.size(file.length()); + builder.modificationDate(new Date(file.lastModified())); + } + } + return builder.build(); + } +} diff --git a/contribs/jersey-multipart/src/test/java/com/sun/jersey/multipart/FileDataBodyPartTest.java b/contribs/jersey-multipart/src/test/java/com/sun/jersey/multipart/FileDataBodyPartTest.java new file mode 100644 index 0000000..c702f25 --- /dev/null +++ b/contribs/jersey-multipart/src/test/java/com/sun/jersey/multipart/FileDataBodyPartTest.java @@ -0,0 +1,144 @@ +/* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can obtain + * a copy of the License at https://jersey.dev.java.net/CDDL+GPL.html + * or jersey/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at jersey/legal/LICENSE.txt. + * Sun designates this particular file as subject to the "Classpath" exception + * as provided by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the License + * Header, with the fields enclosed by brackets [] replaced by your own + * identifying information: "Portions Copyrighted [year] + * [name of copyright owner]" + * + * Contributor(s): + * + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package com.sun.jersey.multipart; + +import java.io.File; +import javax.ws.rs.core.MediaType; + +/** + *

Test case for {@link BodyPart}.

+ */ +public class FileDataBodyPartTest + extends BodyPartTest { + + public FileDataBodyPartTest(String testName) { + super(testName); + } + + @Override + protected void setUp() + throws Exception { + super.setUp(); + bodyPart = new FileDataBodyPart(); + } + + @Override + protected void tearDown() + throws Exception { + bodyPart = null; + super.tearDown(); + } + + @Override + public void testEntity() { + try { + bodyPart.setEntity("foo bar baz"); + } + catch (UnsupportedOperationException exception) { + //exception expected. + } + } + + public void testCreateFDBP() { + + FileDataBodyPart fdbp = (FileDataBodyPart) bodyPart; + assertNull(fdbp.getFormDataContentDisposition()); + assertNull(fdbp.getName()); + assertNull(fdbp.getValue()); + assertTrue(fdbp.isSimple()); + String name; + + File file = new File("pom.xml"); + fdbp = new FileDataBodyPart(file); + MediaType expectedType = MediaType.APPLICATION_XML_TYPE; + checkEntityAttributes(null, fdbp, file, expectedType); + name = "xml"; + fdbp.setName(name); + checkEntityAttributes(name, fdbp, file, expectedType); + fdbp = new FileDataBodyPart(name, file); + checkEntityAttributes(name, fdbp, file, expectedType); + fdbp = new FileDataBodyPart(name, file, MediaType.APPLICATION_XML_TYPE); + checkEntityAttributes(name, fdbp, file, expectedType); + + file = new File("pom.png"); + fdbp = new FileDataBodyPart(file); + expectedType = CommonMediaType.PNG.getMediaType(); + checkEntityAttributes(null, fdbp, file, expectedType); + name = "png"; + fdbp.setName(name); + checkEntityAttributes(name, fdbp, file, expectedType); + + file = new File("pom.zip"); + fdbp = new FileDataBodyPart(file); + expectedType = CommonMediaType.ZIP.getMediaType(); + checkEntityAttributes(null, fdbp, file, expectedType); + + file = new File("pom.avi"); + fdbp = new FileDataBodyPart(file); + expectedType = CommonMediaType.AVI.getMediaType(); + checkEntityAttributes(null, fdbp, file, expectedType); + + } + + private void checkEntityAttributes(final String name, + final FileDataBodyPart fdbp, + final File file, + final MediaType expectedType) { + if (name != null) { + assertEquals(name, fdbp.getName()); + assertEquals(name, fdbp.getFormDataContentDisposition().getName()); + assertEquals(file.getName(), fdbp.getContentDisposition(). + getFileName()); + if (file.exists()) { + assertEquals(file.length(), + fdbp.getContentDisposition().getSize()); + assertEquals(file.lastModified(), + fdbp.getContentDisposition().getModificationDate().getTime()); + } + else { + assertEquals(-1, fdbp.getContentDisposition().getSize()); + } + } + else { + assertNull(fdbp.getName()); + assertNull(fdbp.getFormDataContentDisposition()); + } + assertEquals(file, fdbp.getEntity()); + assertTrue(!fdbp.isSimple()); + assertEquals(expectedType, fdbp.getMediaType()); + } +} -- 1.5.6