Что-то не так со встроенным Image.getScaledInstance (w, h, hints)? (*)
Используйте подсказки = Image.SCALE_SMOOTH, чтобы получить не ужасные миниатюры. Затем используйте ImageIO для преобразования в требуемый формат для вывода.
*: ну да, с этим что-то не так, он немного медленный, но на самом деле, учитывая все другие проблемы, связанные с работой в Интернете, это вряд ли является большой проблемой. Это также не самое лучшее качество для масштабирования изображений, где лучше использовать drawImage с визуализацией BICUBIC. Но вы говорите о даунскейлинге только сейчас.
Обязательно проверьте переданные размеры, чтобы вы не могли сделать свой сервлет DoS, передавая огромные размеры, вызывая создание огромного объема памяти.