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.web.multipart.support;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.List;
022import javax.servlet.http.HttpServletRequest;
023import javax.servlet.http.Part;
024
025import org.springframework.core.MethodParameter;
026import org.springframework.core.ResolvableType;
027import org.springframework.util.ClassUtils;
028import org.springframework.web.multipart.MultipartException;
029import org.springframework.web.multipart.MultipartFile;
030import org.springframework.web.multipart.MultipartHttpServletRequest;
031import org.springframework.web.util.WebUtils;
032
033/**
034 * A common delegate for {@code HandlerMethodArgumentResolver} implementations
035 * which need to resolve {@link MultipartFile} and {@link Part} arguments.
036 *
037 * @author Juergen Hoeller
038 * @since 4.3
039 */
040public abstract class MultipartResolutionDelegate {
041
042        public static final Object UNRESOLVABLE = new Object();
043
044
045        private static Class<?> servletPartClass = null;
046
047        static {
048                try {
049                        servletPartClass = ClassUtils.forName("javax.servlet.http.Part",
050                                        MultipartResolutionDelegate.class.getClassLoader());
051                }
052                catch (ClassNotFoundException ex) {
053                        // Servlet 3.0 javax.servlet.http.Part type not available -
054                        // Part references simply not supported then.
055                }
056        }
057
058
059        public static boolean isMultipartRequest(HttpServletRequest request) {
060                return (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null ||
061                                isMultipartContent(request));
062        }
063
064        private static boolean isMultipartContent(HttpServletRequest request) {
065                String contentType = request.getContentType();
066                return (contentType != null && contentType.toLowerCase().startsWith("multipart/"));
067        }
068
069        static MultipartHttpServletRequest asMultipartHttpServletRequest(HttpServletRequest request) {
070                MultipartHttpServletRequest unwrapped = WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
071                if (unwrapped != null) {
072                        return unwrapped;
073                }
074                return adaptToMultipartHttpServletRequest(request);
075        }
076
077        private static MultipartHttpServletRequest adaptToMultipartHttpServletRequest(HttpServletRequest request) {
078                if (servletPartClass != null) {
079                        // Servlet 3.0 available ..
080                        return new StandardMultipartHttpServletRequest(request);
081                }
082                throw new MultipartException("Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
083        }
084
085
086        public static boolean isMultipartArgument(MethodParameter parameter) {
087                Class<?> paramType = parameter.getNestedParameterType();
088                return (MultipartFile.class == paramType ||
089                                isMultipartFileCollection(parameter) || isMultipartFileArray(parameter) ||
090                                (servletPartClass != null && (servletPartClass == paramType ||
091                                                isPartCollection(parameter) || isPartArray(parameter))));
092        }
093
094        public static Object resolveMultipartArgument(String name, MethodParameter parameter, HttpServletRequest request)
095                        throws Exception {
096
097                MultipartHttpServletRequest multipartRequest =
098                                WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
099                boolean isMultipart = (multipartRequest != null || isMultipartContent(request));
100
101                if (MultipartFile.class == parameter.getNestedParameterType()) {
102                        if (multipartRequest == null && isMultipart) {
103                                multipartRequest = adaptToMultipartHttpServletRequest(request);
104                        }
105                        return (multipartRequest != null ? multipartRequest.getFile(name) : null);
106                }
107                else if (isMultipartFileCollection(parameter)) {
108                        if (multipartRequest == null && isMultipart) {
109                                multipartRequest = adaptToMultipartHttpServletRequest(request);
110                        }
111                        return (multipartRequest != null ? multipartRequest.getFiles(name) : null);
112                }
113                else if (isMultipartFileArray(parameter)) {
114                        if (multipartRequest == null && isMultipart) {
115                                multipartRequest = adaptToMultipartHttpServletRequest(request);
116                        }
117                        if (multipartRequest != null) {
118                                List<MultipartFile> multipartFiles = multipartRequest.getFiles(name);
119                                return multipartFiles.toArray(new MultipartFile[multipartFiles.size()]);
120                        }
121                        else {
122                                return null;
123                        }
124                }
125                else if (servletPartClass != null) {
126                        if (servletPartClass == parameter.getNestedParameterType()) {
127                                return (isMultipart ? RequestPartResolver.resolvePart(request, name) : null);
128                        }
129                        else if (isPartCollection(parameter)) {
130                                return (isMultipart ? RequestPartResolver.resolvePartList(request, name) : null);
131                        }
132                        else if (isPartArray(parameter)) {
133                                return (isMultipart ? RequestPartResolver.resolvePartArray(request, name) : null);
134                        }
135                }
136                return UNRESOLVABLE;
137        }
138
139        private static boolean isMultipartFileCollection(MethodParameter methodParam) {
140                return (MultipartFile.class == getCollectionParameterType(methodParam));
141        }
142
143        private static boolean isMultipartFileArray(MethodParameter methodParam) {
144                return (MultipartFile.class == methodParam.getNestedParameterType().getComponentType());
145        }
146
147        private static boolean isPartCollection(MethodParameter methodParam) {
148                return (servletPartClass == getCollectionParameterType(methodParam));
149        }
150
151        private static boolean isPartArray(MethodParameter methodParam) {
152                return (servletPartClass == methodParam.getNestedParameterType().getComponentType());
153        }
154
155        private static Class<?> getCollectionParameterType(MethodParameter methodParam) {
156                Class<?> paramType = methodParam.getNestedParameterType();
157                if (Collection.class == paramType || List.class.isAssignableFrom(paramType)){
158                        Class<?> valueType = ResolvableType.forMethodParameter(methodParam).asCollection().resolveGeneric();
159                        if (valueType != null) {
160                                return valueType;
161                        }
162                }
163                return null;
164        }
165
166
167        /**
168         * Inner class to avoid hard-coded dependency on Servlet 3.0 Part type...
169         */
170        private static class RequestPartResolver {
171
172                public static Object resolvePart(HttpServletRequest servletRequest, String name) throws Exception {
173                        return servletRequest.getPart(name);
174                }
175
176                public static Object resolvePartList(HttpServletRequest servletRequest, String name) throws Exception {
177                        Collection<Part> parts = servletRequest.getParts();
178                        List<Part> result = new ArrayList<Part>(parts.size());
179                        for (Part part : parts) {
180                                if (part.getName().equals(name)) {
181                                        result.add(part);
182                                }
183                        }
184                        return result;
185                }
186
187                public static Object resolvePartArray(HttpServletRequest servletRequest, String name) throws Exception {
188                        Collection<Part> parts = servletRequest.getParts();
189                        List<Part> result = new ArrayList<Part>(parts.size());
190                        for (Part part : parts) {
191                                if (part.getName().equals(name)) {
192                                        result.add(part);
193                                }
194                        }
195                        return result.toArray(new Part[result.size()]);
196                }
197        }
198
199}