001/* 002 * Copyright 2002-2020 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.core.codec; 018 019import java.io.ByteArrayInputStream; 020import java.util.Map; 021 022import org.reactivestreams.Publisher; 023import reactor.core.publisher.Flux; 024 025import org.springframework.core.ResolvableType; 026import org.springframework.core.io.ByteArrayResource; 027import org.springframework.core.io.InputStreamResource; 028import org.springframework.core.io.Resource; 029import org.springframework.core.io.buffer.DataBuffer; 030import org.springframework.core.io.buffer.DataBufferUtils; 031import org.springframework.lang.Nullable; 032import org.springframework.util.MimeType; 033import org.springframework.util.MimeTypeUtils; 034 035/** 036 * Decoder for {@link Resource Resources}. 037 * 038 * @author Arjen Poutsma 039 * @author Rossen Stoyanchev 040 * @since 5.0 041 */ 042public class ResourceDecoder extends AbstractDataBufferDecoder<Resource> { 043 044 /** Name of hint with a filename for the resource(e.g. from "Content-Disposition" HTTP header). */ 045 public static String FILENAME_HINT = ResourceDecoder.class.getName() + ".filename"; 046 047 048 public ResourceDecoder() { 049 super(MimeTypeUtils.ALL); 050 } 051 052 053 @Override 054 public boolean canDecode(ResolvableType elementType, @Nullable MimeType mimeType) { 055 return (Resource.class.isAssignableFrom(elementType.toClass()) && 056 super.canDecode(elementType, mimeType)); 057 } 058 059 @Override 060 public Flux<Resource> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType, 061 @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { 062 063 return Flux.from(decodeToMono(inputStream, elementType, mimeType, hints)); 064 } 065 066 @Override 067 public Resource decode(DataBuffer dataBuffer, ResolvableType elementType, 068 @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { 069 070 byte[] bytes = new byte[dataBuffer.readableByteCount()]; 071 dataBuffer.read(bytes); 072 DataBufferUtils.release(dataBuffer); 073 074 if (logger.isDebugEnabled()) { 075 logger.debug(Hints.getLogPrefix(hints) + "Read " + bytes.length + " bytes"); 076 } 077 078 Class<?> clazz = elementType.toClass(); 079 String filename = hints != null ? (String) hints.get(FILENAME_HINT) : null; 080 if (clazz == InputStreamResource.class) { 081 return new InputStreamResource(new ByteArrayInputStream(bytes)) { 082 @Override 083 public String getFilename() { 084 return filename; 085 } 086 @Override 087 public long contentLength() { 088 return bytes.length; 089 } 090 }; 091 } 092 else if (Resource.class.isAssignableFrom(clazz)) { 093 return new ByteArrayResource(bytes) { 094 @Override 095 public String getFilename() { 096 return filename; 097 } 098 }; 099 } 100 else { 101 throw new IllegalStateException("Unsupported resource class: " + clazz); 102 } 103 } 104 105}