Browse Source
git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@3076 50f2f4bb-b051-0410-bef5-90022cba6387pull/1/head
20 changed files with 614 additions and 377 deletions
@ -0,0 +1,147 @@
@@ -0,0 +1,147 @@
|
||||
/* |
||||
* Copyright 2002-2010 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.http.converter; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import javax.activation.FileTypeMap; |
||||
import javax.activation.MimetypesFileTypeMap; |
||||
|
||||
import org.springframework.core.io.ByteArrayResource; |
||||
import org.springframework.core.io.ClassPathResource; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.HttpInputMessage; |
||||
import org.springframework.http.HttpOutputMessage; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.util.ClassUtils; |
||||
import org.springframework.util.FileCopyUtils; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* Implementation of {@link HttpMessageConverter} that can read and write {@link Resource Resources}. |
||||
* |
||||
* <p>By default, this converter can read all media types. The Java Activation Framework (JAF) - if available - is used |
||||
* to determine the {@code Content-Type} of written resources. If JAF is not available, {@code application/octet-stream} |
||||
* is used. |
||||
* |
||||
* @author Arjen Poutsma |
||||
* @since 3.0.2 |
||||
*/ |
||||
public class ResourceHttpMessageConverter implements HttpMessageConverter<Resource> { |
||||
|
||||
private static final boolean jafPresent = |
||||
ClassUtils.isPresent("javax.activation.FileTypeMap", ResourceHttpMessageConverter.class.getClassLoader()); |
||||
|
||||
public boolean canRead(Class<?> clazz, MediaType mediaType) { |
||||
return Resource.class.isAssignableFrom(clazz); |
||||
} |
||||
|
||||
public boolean canWrite(Class<?> clazz, MediaType mediaType) { |
||||
return Resource.class.isAssignableFrom(clazz); |
||||
} |
||||
|
||||
public List<MediaType> getSupportedMediaTypes() { |
||||
return Collections.singletonList(MediaType.ALL); |
||||
} |
||||
|
||||
public Resource read(Class<? extends Resource> clazz, HttpInputMessage inputMessage) |
||||
throws IOException, HttpMessageNotReadableException { |
||||
byte[] body = FileCopyUtils.copyToByteArray(inputMessage.getBody()); |
||||
return new ByteArrayResource(body); |
||||
} |
||||
|
||||
public void write(Resource resource, MediaType contentType, HttpOutputMessage outputMessage) |
||||
throws IOException, HttpMessageNotWritableException { |
||||
HttpHeaders headers = outputMessage.getHeaders(); |
||||
if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) { |
||||
contentType = getContentType(resource); |
||||
} |
||||
if (contentType != null) { |
||||
headers.setContentType(contentType); |
||||
} |
||||
Long contentLength = getContentLength(resource, contentType); |
||||
if (contentLength != null) { |
||||
headers.setContentLength(contentLength); |
||||
} |
||||
FileCopyUtils.copy(resource.getInputStream(), outputMessage.getBody()); |
||||
outputMessage.getBody().flush(); |
||||
} |
||||
|
||||
private MediaType getContentType(Resource resource) { |
||||
if (jafPresent) { |
||||
return ActivationMediaTypeFactory.getMediaType(resource); |
||||
} else { |
||||
return MediaType.APPLICATION_OCTET_STREAM; |
||||
} |
||||
} |
||||
|
||||
protected Long getContentLength(Resource resource, MediaType contentType) { |
||||
try { |
||||
return resource.getFile().length(); |
||||
} |
||||
catch (IOException e) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Inner class to avoid hard-coded JAF dependency. |
||||
*/ |
||||
private static class ActivationMediaTypeFactory { |
||||
|
||||
private static final FileTypeMap fileTypeMap; |
||||
|
||||
static { |
||||
fileTypeMap = loadFileTypeMapFromContextSupportModule(); |
||||
} |
||||
|
||||
private static FileTypeMap loadFileTypeMapFromContextSupportModule() { |
||||
// see if we can find the extended mime.types from the context-support module
|
||||
Resource mappingLocation = new ClassPathResource("org/springframework/mail/javamail/mime.types"); |
||||
if (mappingLocation.exists()) { |
||||
InputStream inputStream = null; |
||||
try { |
||||
inputStream = mappingLocation.getInputStream(); |
||||
return new MimetypesFileTypeMap(inputStream); |
||||
} |
||||
catch (IOException ex) { |
||||
// ignore
|
||||
} |
||||
finally { |
||||
if (inputStream != null) { |
||||
try { |
||||
inputStream.close(); |
||||
} |
||||
catch (IOException ex) { |
||||
// ignore
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
return FileTypeMap.getDefaultFileTypeMap(); |
||||
} |
||||
|
||||
public static MediaType getMediaType(Resource resource) { |
||||
String mediaType = fileTypeMap.getContentType(resource.getFilename()); |
||||
return StringUtils.hasText(mediaType) ? MediaType.parseMediaType(mediaType) : null; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,103 +0,0 @@
@@ -1,103 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2010 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.http.converter.multipart; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
import java.io.UnsupportedEncodingException; |
||||
|
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* <p>Inspired by {@link org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity}. |
||||
* |
||||
* @author Arjen Poutsma |
||||
* @since 3.0.2 |
||||
*/ |
||||
abstract class AbstractPart implements Part { |
||||
|
||||
private static final byte[] CONTENT_DISPOSITION = |
||||
new byte[]{'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'D', 'i', 's', 'p', 'o', 's', 'i', 't', 'i', 'o', 'n', |
||||
':', ' ', 'f', 'o', 'r', 'm', '-', 'd', 'a', 't', 'a', ';', ' ', 'n', 'a', 'm', 'e', '='}; |
||||
|
||||
private static final byte[] CONTENT_TYPE = |
||||
new byte[]{'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'T', 'y', 'p', 'e', ':', ' '}; |
||||
|
||||
private final MediaType contentType; |
||||
|
||||
protected AbstractPart(MediaType contentType) { |
||||
Assert.notNull(contentType, "'contentType' must not be null"); |
||||
this.contentType = contentType; |
||||
} |
||||
|
||||
public final void write(byte[] boundary, String name, OutputStream os) throws IOException { |
||||
writeBoundary(boundary, os); |
||||
writeContentDisposition(name, os); |
||||
writeContentType(os); |
||||
writeEndOfHeader(os); |
||||
writeData(os); |
||||
writeEnd(os); |
||||
} |
||||
|
||||
protected void writeBoundary(byte[] boundary, OutputStream os) throws IOException { |
||||
os.write('-'); |
||||
os.write('-'); |
||||
os.write(boundary); |
||||
writeNewLine(os); |
||||
} |
||||
|
||||
protected void writeContentDisposition(String name, OutputStream os) throws IOException { |
||||
os.write(CONTENT_DISPOSITION); |
||||
os.write('"'); |
||||
os.write(getAsciiBytes(name)); |
||||
os.write('"'); |
||||
} |
||||
|
||||
protected void writeContentType(OutputStream os) throws IOException { |
||||
writeNewLine(os); |
||||
os.write(CONTENT_TYPE); |
||||
os.write(getAsciiBytes(contentType.toString())); |
||||
} |
||||
|
||||
protected byte[] getAsciiBytes(String name) { |
||||
try { |
||||
return name.getBytes("US-ASCII"); |
||||
} |
||||
catch (UnsupportedEncodingException ex) { |
||||
// should not happen, US-ASCII is always supported
|
||||
throw new IllegalStateException(ex); |
||||
} |
||||
} |
||||
|
||||
protected void writeEndOfHeader(OutputStream os) throws IOException { |
||||
writeNewLine(os); |
||||
writeNewLine(os); |
||||
} |
||||
|
||||
protected void writeEnd(OutputStream os) throws IOException { |
||||
writeNewLine(os); |
||||
} |
||||
|
||||
private void writeNewLine(OutputStream os) throws IOException { |
||||
os.write('\r'); |
||||
os.write('\n'); |
||||
} |
||||
|
||||
protected abstract void writeData(OutputStream os) throws IOException; |
||||
|
||||
} |
||||
@ -1,44 +0,0 @@
@@ -1,44 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2010 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.http.converter.multipart; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
|
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.FileCopyUtils; |
||||
|
||||
/** |
||||
* @author Arjen Poutsma |
||||
* @since 3.0.2 |
||||
*/ |
||||
class ByteArrayPart extends AbstractPart { |
||||
|
||||
private final byte[] value; |
||||
|
||||
public ByteArrayPart(byte[] value, MediaType contentType) { |
||||
super(contentType); |
||||
Assert.isTrue(value != null && value.length != 0, "'value' must not be null"); |
||||
this.value = value; |
||||
} |
||||
|
||||
@Override |
||||
protected void writeData(OutputStream os) throws IOException { |
||||
FileCopyUtils.copy(value, os); |
||||
} |
||||
} |
||||
@ -0,0 +1,89 @@
@@ -0,0 +1,89 @@
|
||||
/* |
||||
* Copyright 2002-2010 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.http.converter.multipart; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
import java.io.UnsupportedEncodingException; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.HttpOutputMessage; |
||||
|
||||
/** |
||||
* Implementation of {@link HttpOutputMessage} used for writing multipart data. |
||||
* |
||||
* @author Arjen Poutsma |
||||
* @since 3.0.2 |
||||
*/ |
||||
class MultipartHttpOutputMessage implements HttpOutputMessage { |
||||
|
||||
private final HttpHeaders headers = new HttpHeaders(); |
||||
|
||||
private final OutputStream os; |
||||
|
||||
private boolean headersWritten = false; |
||||
|
||||
public MultipartHttpOutputMessage(OutputStream os) { |
||||
this.os = os; |
||||
} |
||||
|
||||
public HttpHeaders getHeaders() { |
||||
return headersWritten ? HttpHeaders.readOnlyHttpHeaders(headers) : this.headers; |
||||
} |
||||
|
||||
public OutputStream getBody() throws IOException { |
||||
writeHeaders(); |
||||
return this.os; |
||||
} |
||||
|
||||
private void writeHeaders() throws IOException { |
||||
if (!this.headersWritten) { |
||||
for (Map.Entry<String, List<String>> entry : this.headers.entrySet()) { |
||||
byte[] headerName = getAsciiBytes(entry.getKey()); |
||||
for (String headerValueString : entry.getValue()) { |
||||
byte[] headerValue = getAsciiBytes(headerValueString); |
||||
os.write(headerName); |
||||
os.write(':'); |
||||
os.write(' '); |
||||
os.write(headerValue); |
||||
writeNewLine(os); |
||||
} |
||||
} |
||||
writeNewLine(os); |
||||
this.headersWritten = true; |
||||
} |
||||
} |
||||
|
||||
private void writeNewLine(OutputStream os) throws IOException { |
||||
os.write('\r'); |
||||
os.write('\n'); |
||||
} |
||||
|
||||
protected byte[] getAsciiBytes(String name) { |
||||
try { |
||||
return name.getBytes("US-ASCII"); |
||||
} |
||||
catch (UnsupportedEncodingException ex) { |
||||
// should not happen, US-ASCII is always supported
|
||||
throw new IllegalStateException(ex); |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
||||
@ -1,30 +0,0 @@
@@ -1,30 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2010 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.http.converter.multipart; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
|
||||
/** |
||||
* @author Arjen Poutsma |
||||
* @since 3.0.2 |
||||
*/ |
||||
public interface Part { |
||||
|
||||
void write(byte[] boundary, String name, OutputStream os) throws IOException; |
||||
|
||||
} |
||||
@ -1,58 +0,0 @@
@@ -1,58 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2010 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.http.converter.multipart; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
|
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.FileCopyUtils; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** @author Arjen Poutsma */ |
||||
class ResourcePart extends AbstractPart { |
||||
|
||||
private static final byte[] FILE_NAME = new byte[]{';', ' ', 'f', 'i', 'l', 'e', 'n', 'a', 'm', 'e', '='}; |
||||
|
||||
private final Resource resource; |
||||
|
||||
public ResourcePart(Resource resource) { |
||||
super(new MediaType("application", "octet-stream")); |
||||
Assert.notNull(resource, "'resource' must not be null"); |
||||
Assert.isTrue(resource.exists(), "'" + resource + "' does not exist"); |
||||
this.resource = resource; |
||||
} |
||||
|
||||
@Override |
||||
protected void writeContentDisposition(String name, OutputStream os) throws IOException { |
||||
super.writeContentDisposition(name, os); |
||||
String filename = resource.getFilename(); |
||||
if (StringUtils.hasLength(filename)) { |
||||
os.write(FILE_NAME); |
||||
os.write('"'); |
||||
os.write(getAsciiBytes(filename)); |
||||
os.write('"'); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void writeData(OutputStream os) throws IOException { |
||||
FileCopyUtils.copy(resource.getInputStream(), os); |
||||
} |
||||
} |
||||
@ -1,54 +0,0 @@
@@ -1,54 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2010 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.http.converter.multipart; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
import java.io.OutputStreamWriter; |
||||
import java.nio.charset.Charset; |
||||
|
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.FileCopyUtils; |
||||
|
||||
/** @author Arjen Poutsma */ |
||||
class StringPart extends AbstractPart { |
||||
|
||||
private static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1"); |
||||
|
||||
private final String value; |
||||
|
||||
private final Charset charset; |
||||
|
||||
public StringPart(String value) { |
||||
this(value, DEFAULT_CHARSET); |
||||
} |
||||
|
||||
public StringPart(String value, Charset charset) { |
||||
super(new MediaType("text", "plain", charset)); |
||||
Assert.hasText(value, "'value' must not be null"); |
||||
Assert.notNull(charset, "'charset' must not be null"); |
||||
this.value = value; |
||||
this.charset = charset; |
||||
} |
||||
|
||||
@Override |
||||
protected void writeData(OutputStream os) throws IOException { |
||||
FileCopyUtils.copy(value, new OutputStreamWriter(os, charset)); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
/* |
||||
* Copyright 2002-2010 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.http.converter; |
||||
|
||||
import java.io.IOException; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.core.io.ClassPathResource; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.http.MockHttpInputMessage; |
||||
import org.springframework.http.MockHttpOutputMessage; |
||||
import org.springframework.util.FileCopyUtils; |
||||
|
||||
/** |
||||
* @author Arjen Poutsma |
||||
*/ |
||||
public class ResourceHttpMessageConverterTests { |
||||
|
||||
private ResourceHttpMessageConverter converter; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
converter = new ResourceHttpMessageConverter(); |
||||
} |
||||
|
||||
@Test |
||||
public void canRead() { |
||||
assertTrue(converter.canRead(Resource.class, new MediaType("application", "octet-stream"))); |
||||
} |
||||
|
||||
@Test |
||||
public void canWrite() { |
||||
assertTrue(converter.canWrite(Resource.class, new MediaType("application", "octet-stream"))); |
||||
assertTrue(converter.canWrite(Resource.class, MediaType.ALL)); |
||||
} |
||||
|
||||
@Test |
||||
public void read() throws IOException { |
||||
byte[] body = FileCopyUtils.copyToByteArray(getClass().getResourceAsStream("logo.jpg")); |
||||
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body); |
||||
inputMessage.getHeaders().setContentType(MediaType.IMAGE_JPEG); |
||||
converter.read(Resource.class, inputMessage); |
||||
} |
||||
|
||||
@Test |
||||
public void write() throws IOException { |
||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); |
||||
Resource body = new ClassPathResource("logo.jpg", getClass()); |
||||
converter.write(body, null, outputMessage); |
||||
assertEquals("Invalid content-type", MediaType.IMAGE_JPEG, |
||||
outputMessage.getHeaders().getContentType()); |
||||
assertEquals("Invalid content-length", body.getFile().length(), outputMessage.getHeaders().getContentLength()); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue