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}