Как использовать массив @RequestParam для нескольких типов данных?

Я создаю контроллер загрузки Spring с массивом @RequestParam. Дело в том, что входные данные будут представлять собой смесь загрузки изображения (нового изображения) и строки (идентификатора существующего изображения) в теле запроса в виде FormData.

Если это новое изображение, мне нужно загрузить его и получить его идентификатор. Если это существующее изображение, я просто использую тот же идентификатор из входных данных.

В контроллере я попробовал использовать

@RequestParam Object[] images

и

if (images[i] instanceof String) {
// Existing Image
} else if (images[i] instanceof MultipartFile) {
// New Image
}

Это работает, если все элементы являются изображениями или всеми элементами или строкой. Но если это смесь того и другого, контроллер захватывает только MultipartFile.

Когда я проверяю вкладку «Сеть» (Браузер), все строки и файлы загружаются нормально.

Может ли кто-нибудь помочь мне с захватом разных типов?

1
0
54
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

@RequestParam используется для обработки простых типов данных и, вероятно, не может обрабатывать сложные типы, которые вам нужны, в вашем случае изображение и строку. Вы можете попробовать использовать @RequestPart вместо @RequestParam.

@RequestPart("images") Object[] images

if (images[i] instanceof String) {
    // Existing Image
} else if (images[i] instanceof MultipartFile) {
    // New Image
}

Это позволит вам обрабатывать несколько типов данных, полученных от клиента.

Попробовал, но получил это исключение. HttpMediaTypeNotSupportedException: тип контента «приложение/октет-поток» не поддерживается

Karthikeyan K 08.07.2024 14:07
Ответ принят как подходящий

Классный вопрос, Бро,

Ты прав. Ваш метод только что получил все файлы или все строки, поскольку логика метода resolveName в org.springframework.web.method.annotation.RequestParamMethodArgumentResolver. Значения параметров будут игнорироваться, если файлы состоят из нескольких частей:

Object arg = null;
MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
if (multipartRequest != null) {
    List<MultipartFile> files = multipartRequest.getFiles(name);
    if (!files.isEmpty()) {
        arg = (files.size() == 1 ? files.get(0) : files);
    }
}
if (arg == null) {
    String[] paramValues = request.getParameterValues(name);
    if (paramValues != null) {
        arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
    }
}
return arg;

Я только что нашел решение после того, как потратил время на эту статью Пользовательская привязка данных в Spring MVC и несколько интересных классов Spring.

Первым делом создаем разметочную аннотацию @MixMultipartFileAndString

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface MixMultipartFileAndString {
}

Далее мы создаем индивидуальный HandlerMethodArgumentResolver, который принимает все составные файлы и строку параметров:


public class MixMultipartFileAndStringArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.getParameterAnnotation(MixMultipartFileAndString.class) != null;
    }

    /**
     *
     * @param methodParameter the method parameter to resolve. This parameter must
     * have previously been passed to {@link #supportsParameter} which must
     * have returned {@code true}.
     * @param mavContainer the ModelAndViewContainer for the current request
     * @param request the current request
     * @param binderFactory a factory for creating {@link WebDataBinder} instances
     * @return
     * @throws Exception
     * @see org.springframework.web.method.annotation.RequestParamMethodArgumentResolver#resolveName(String, MethodParameter, NativeWebRequest)
     */
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer mavContainer, NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
        String name = methodParameter.getParameterName();
        if (servletRequest != null) {
            Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, methodParameter, servletRequest);
            if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
                return mpArg;
            }
        }

        List<Object> arg = new ArrayList<>();
        MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
        if (multipartRequest != null) {
            List<MultipartFile> files = multipartRequest.getFiles(name);
            if (!files.isEmpty()) {
                arg.addAll(files);
            }
        }

        String[] paramValues = request.getParameterValues(name);
        if (paramValues != null) {
            Collections.addAll(arg, paramValues);
        }
        return arg;
    }
}

И тогда нам нужно зарегистрироваться:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
   @Override
   public void addArgumentResolvers(
           List<HandlerMethodArgumentResolver> argumentResolvers) {
      argumentResolvers.add( new MixMultipartFileAndStringArgumentResolver());
   }
}

Наконец, используйте его в нашем контроллере:

@PostMapping("/api")
public void uploadOrGetImages(@MixMultipartFileAndString List<Object> images) {
    for (Object object: images) {
        if (object instanceof MultipartFile) {}
        if (object instanceof String) {}
    }
}

Надеюсь, поможет.

Другие вопросы по теме