001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.http.converter; 018 019import java.io.FileNotFoundException; 020import java.io.IOException; 021import java.io.InputStream; 022 023import org.springframework.core.io.ByteArrayResource; 024import org.springframework.core.io.InputStreamResource; 025import org.springframework.core.io.Resource; 026import org.springframework.http.HttpInputMessage; 027import org.springframework.http.HttpOutputMessage; 028import org.springframework.http.MediaType; 029import org.springframework.util.ClassUtils; 030import org.springframework.util.StreamUtils; 031 032/** 033 * Implementation of {@link HttpMessageConverter} that can read/write {@link Resource Resources} 034 * and supports byte range requests. 035 * 036 * <p>By default, this converter can read all media types. The Java Activation Framework (JAF) - 037 * if available - is used to determine the {@code Content-Type} of written resources. 038 * If JAF is not available, {@code application/octet-stream} is used. 039 * 040 * @author Arjen Poutsma 041 * @author Juergen Hoeller 042 * @author Kazuki Shimizu 043 * @since 3.0.2 044 */ 045public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<Resource> { 046 047 private static final boolean jafPresent = ClassUtils.isPresent( 048 "javax.activation.FileTypeMap", ResourceHttpMessageConverter.class.getClassLoader()); 049 050 051 public ResourceHttpMessageConverter() { 052 super(MediaType.ALL); 053 } 054 055 056 @Override 057 protected boolean supports(Class<?> clazz) { 058 return Resource.class.isAssignableFrom(clazz); 059 } 060 061 @Override 062 protected Resource readInternal(Class<? extends Resource> clazz, HttpInputMessage inputMessage) 063 throws IOException, HttpMessageNotReadableException { 064 065 if (InputStreamResource.class == clazz) { 066 return new InputStreamResource(inputMessage.getBody()); 067 } 068 else if (clazz.isAssignableFrom(ByteArrayResource.class)) { 069 byte[] body = StreamUtils.copyToByteArray(inputMessage.getBody()); 070 return new ByteArrayResource(body); 071 } 072 else { 073 throw new IllegalStateException("Unsupported resource class: " + clazz); 074 } 075 } 076 077 @Override 078 protected MediaType getDefaultContentType(Resource resource) { 079 if (jafPresent) { 080 return ActivationMediaTypeFactory.getMediaType(resource); 081 } 082 else { 083 return MediaType.APPLICATION_OCTET_STREAM; 084 } 085 } 086 087 @Override 088 protected Long getContentLength(Resource resource, MediaType contentType) throws IOException { 089 // Don't try to determine contentLength on InputStreamResource - cannot be read afterwards... 090 // Note: custom InputStreamResource subclasses could provide a pre-calculated content length! 091 if (InputStreamResource.class == resource.getClass()) { 092 return null; 093 } 094 long contentLength = resource.contentLength(); 095 return (contentLength < 0 ? null : contentLength); 096 } 097 098 @Override 099 protected void writeInternal(Resource resource, HttpOutputMessage outputMessage) 100 throws IOException, HttpMessageNotWritableException { 101 102 writeContent(resource, outputMessage); 103 } 104 105 protected void writeContent(Resource resource, HttpOutputMessage outputMessage) 106 throws IOException, HttpMessageNotWritableException { 107 try { 108 InputStream in = resource.getInputStream(); 109 try { 110 StreamUtils.copy(in, outputMessage.getBody()); 111 } 112 catch (NullPointerException ex) { 113 // ignore, see SPR-13620 114 } 115 finally { 116 try { 117 in.close(); 118 } 119 catch (Throwable ex) { 120 // ignore, see SPR-12999 121 } 122 } 123 } 124 catch (FileNotFoundException ex) { 125 // ignore, see SPR-12999 126 } 127 } 128 129}