All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.http.converter.BufferedImageHttpMessageConverter Maven / Gradle / Ivy

There is a newer version: 6.2.0
Show newest version
/*
 * Copyright 2002-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.http.converter;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileCacheImageInputStream;
import javax.imageio.stream.FileCacheImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;

import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.StreamingHttpOutputMessage;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;

/**
 * Implementation of {@link HttpMessageConverter} that can read and write
 * {@link BufferedImage BufferedImages}.
 *
 * 

By default, this converter can read all media types that are supported * by the {@linkplain ImageIO#getReaderMIMETypes() registered image readers}, * and writes using the media type of the first available * {@linkplain javax.imageio.ImageIO#getWriterMIMETypes() registered image writer}. * The latter can be overridden by setting the * {@link #setDefaultContentType defaultContentType} property. * *

If the {@link #setCacheDir cacheDir} property is set, this converter * will cache image data. * *

The {@link #process(ImageReadParam)} and {@link #process(ImageWriteParam)} * template methods allow subclasses to override Image I/O parameters. * * @author Arjen Poutsma * @since 3.0 */ public class BufferedImageHttpMessageConverter implements HttpMessageConverter { private final List readableMediaTypes = new ArrayList<>(); @Nullable private MediaType defaultContentType; @Nullable private File cacheDir; public BufferedImageHttpMessageConverter() { String[] readerMediaTypes = ImageIO.getReaderMIMETypes(); for (String mediaType : readerMediaTypes) { if (StringUtils.hasText(mediaType)) { this.readableMediaTypes.add(MediaType.parseMediaType(mediaType)); } } String[] writerMediaTypes = ImageIO.getWriterMIMETypes(); for (String mediaType : writerMediaTypes) { if (StringUtils.hasText(mediaType)) { this.defaultContentType = MediaType.parseMediaType(mediaType); break; } } } /** * Sets the default {@code Content-Type} to be used for writing. * @throws IllegalArgumentException if the given content type is not supported by the Java Image I/O API */ public void setDefaultContentType(@Nullable MediaType defaultContentType) { if (defaultContentType != null) { Iterator imageWriters = ImageIO.getImageWritersByMIMEType(defaultContentType.toString()); if (!imageWriters.hasNext()) { throw new IllegalArgumentException( "Content-Type [" + defaultContentType + "] is not supported by the Java Image I/O API"); } } this.defaultContentType = defaultContentType; } /** * Returns the default {@code Content-Type} to be used for writing. * Called when {@link #write} is invoked without a specified content type parameter. */ @Nullable public MediaType getDefaultContentType() { return this.defaultContentType; } /** * Sets the cache directory. If this property is set to an existing directory, * this converter will cache image data. */ public void setCacheDir(File cacheDir) { Assert.notNull(cacheDir, "'cacheDir' must not be null"); Assert.isTrue(cacheDir.isDirectory(), () -> "'cacheDir' is not a directory: " + cacheDir); this.cacheDir = cacheDir; } @Override public boolean canRead(Class clazz, @Nullable MediaType mediaType) { return (BufferedImage.class == clazz && isReadable(mediaType)); } private boolean isReadable(@Nullable MediaType mediaType) { if (mediaType == null) { return true; } Iterator imageReaders = ImageIO.getImageReadersByMIMEType(mediaType.toString()); return imageReaders.hasNext(); } @Override public boolean canWrite(Class clazz, @Nullable MediaType mediaType) { return (BufferedImage.class == clazz && isWritable(mediaType)); } private boolean isWritable(@Nullable MediaType mediaType) { if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) { return true; } Iterator imageWriters = ImageIO.getImageWritersByMIMEType(mediaType.toString()); return imageWriters.hasNext(); } @Override public List getSupportedMediaTypes() { return Collections.unmodifiableList(this.readableMediaTypes); } @Override public BufferedImage read(@Nullable Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { ImageInputStream imageInputStream = null; ImageReader imageReader = null; // We cannot use try-with-resources here for the ImageInputStream, since we have // custom handling of the close() method in a finally-block. try { imageInputStream = createImageInputStream(inputMessage.getBody()); MediaType contentType = inputMessage.getHeaders().getContentType(); if (contentType == null) { throw new HttpMessageNotReadableException("No Content-Type header", inputMessage); } Iterator imageReaders = ImageIO.getImageReadersByMIMEType(contentType.toString()); if (imageReaders.hasNext()) { imageReader = imageReaders.next(); ImageReadParam irp = imageReader.getDefaultReadParam(); process(irp); imageReader.setInput(imageInputStream, true); return imageReader.read(0, irp); } else { throw new HttpMessageNotReadableException( "Could not find javax.imageio.ImageReader for Content-Type [" + contentType + "]", inputMessage); } } finally { if (imageReader != null) { imageReader.dispose(); } if (imageInputStream != null) { try { imageInputStream.close(); } catch (IOException ex) { // ignore } } } } private ImageInputStream createImageInputStream(InputStream is) throws IOException { is = StreamUtils.nonClosing(is); if (this.cacheDir != null) { return new FileCacheImageInputStream(is, this.cacheDir); } else { return new MemoryCacheImageInputStream(is); } } @Override public void write(final BufferedImage image, @Nullable final MediaType contentType, final HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { final MediaType selectedContentType = getContentType(contentType); outputMessage.getHeaders().setContentType(selectedContentType); if (outputMessage instanceof StreamingHttpOutputMessage streamingOutputMessage) { streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() { @Override public void writeTo(OutputStream outputStream) throws IOException { BufferedImageHttpMessageConverter.this.writeInternal(image, selectedContentType, outputStream); } @Override public boolean repeatable() { return true; } }); } else { writeInternal(image, selectedContentType, outputMessage.getBody()); } } private MediaType getContentType(@Nullable MediaType contentType) { if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) { contentType = getDefaultContentType(); } Assert.notNull(contentType, "Could not select Content-Type. " + "Please specify one through the 'defaultContentType' property."); return contentType; } private void writeInternal(BufferedImage image, MediaType contentType, OutputStream body) throws IOException, HttpMessageNotWritableException { ImageOutputStream imageOutputStream = null; ImageWriter imageWriter = null; try { Iterator imageWriters = ImageIO.getImageWritersByMIMEType(contentType.toString()); if (imageWriters.hasNext()) { imageWriter = imageWriters.next(); ImageWriteParam iwp = imageWriter.getDefaultWriteParam(); process(iwp); imageOutputStream = createImageOutputStream(body); imageWriter.setOutput(imageOutputStream); imageWriter.write(null, new IIOImage(image, null, null), iwp); } else { throw new HttpMessageNotWritableException( "Could not find javax.imageio.ImageWriter for Content-Type [" + contentType + "]"); } } finally { if (imageWriter != null) { imageWriter.dispose(); } if (imageOutputStream != null) { try { imageOutputStream.close(); } catch (IOException ex) { // ignore } } } } private ImageOutputStream createImageOutputStream(OutputStream os) throws IOException { if (this.cacheDir != null) { return new FileCacheImageOutputStream(os, this.cacheDir); } else { return new MemoryCacheImageOutputStream(os); } } /** * Template method that allows for manipulating the {@link ImageReadParam} * before it is used to read an image. *

The default implementation is empty. */ protected void process(ImageReadParam irp) { } /** * Template method that allows for manipulating the {@link ImageWriteParam} * before it is used to write an image. *

The default implementation is empty. */ protected void process(ImageWriteParam iwp) { } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy