Как конвертировать любое изображение в JPG? - PullRequest
5 голосов
/ 08 июля 2020

Я получаю объект MultipartFile Spring от контроллера отдыха. Я пытаюсь преобразовать любой файл inage в JPG изображение , но мне просто нужен массив байтов, чтобы сохранить его на mongoDb

Я нашел этот код для этого

public boolean convertImageToJPG(InputStream attachedFile) {
    try {
        BufferedImage inputImage = ImageIO.read(attachedFile);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        boolean result = ImageIO.write(inputImage, "jpg", byteArrayOutputStream);
        
        return result;
        
    } catch (IOException e) {
        
        System.err.println("Error " + e);
        
    }
    return false;
}

Но result как false без ошибок, поэтому ImageIO.write не работает

Также я обнаружил, что это делает то же самое, но с использованием объекта File, я не хочу create файл в каталоге, мне просто нужен массив байтов

public static boolean convertFormat(String inputImagePath,
        String outputImagePath, String formatName) throws IOException {
        FileInputStream inputStream = new FileInputStream(inputImagePath);
        FileOutputStream outputStream = new FileOutputStream(outputImagePath);
         
        // reads input image from file
        BufferedImage inputImage = ImageIO.read(inputStream);
         
        // writes to the output image in specified format
        boolean result = ImageIO.write(inputImage, formatName, outputStream);
         
        // needs to close the streams
        outputStream.close();
        inputStream.close();
         
        return result;
    }

Тестирование

public class TestImageConverter {
 
    public static void main(String[] args) {
        String inputImage = "D:/Photo/Pic1.jpg";
        String oututImage = "D:/Photo/Pic1.png";
        String formatName = "PNG";
        try {
            boolean result = ImageConverter.convertFormat(inputImage,
                    oututImage, formatName);
            if (result) {
                System.out.println("Image converted successfully.");
            } else {
                System.out.println("Could not convert image.");
            }
        } catch (IOException ex) {
            System.out.println("Error during converting image.");
            ex.printStackTrace();
        }
    }
}

Как я могу решить мою проблему?

1 Ответ

3 голосов
/ 09 июля 2020

ОБНОВЛЕННОЕ РЕШЕНИЕ (альтернатива без необходимости использования Raster и ColorModel)

Меня действительно беспокоило, что мое старое решение (см. Ниже) по-прежнему требовало растров и ColorModels. У меня возникла проблема с моим решением, поэтому я потратил больше времени на поиск альтернатив. Итак, лучшее, что я мог придумать сейчас, - это следующее:

try {
    final FileInputStream fileInputStream = new FileInputStream("dice.png");
    final BufferedImage image = ImageIO.read(fileInputStream);
    fileInputStream.close(); // ImageIO.read does not close the input stream

    final BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
    convertedImage.createGraphics().drawImage(image, 0, 0, Color.WHITE, null);

    final FileOutputStream fileOutputStream = new FileOutputStream("dice-test.jpg");
    final boolean canWrite = ImageIO.write(convertedImage, "jpg", fileOutputStream);
    fileOutputStream.close(); // ImageIO.write does not close the output stream

    if (!canWrite) {
        throw new IllegalStateException("Failed to write image.");
    }
} catch (IOException e) {
    e.printStackTrace();
}

Я получил копию BufferedImage, как и раньше. Он делает более или менее то же самое, но на самом деле вы можете более легко повторно использовать ColorModel и Raster.

drawImage(), похоже, заботится о большей части того, что я делал раньше, вручную. И поскольку это стандартный код библиотеки java, он действительно кажется лучшим способом.

Обратите внимание, что в итоге вы получаете изображение типа BufferedImage.TYPE_INT_RGB. Хотя кажется, что это работает для типов jpg, png и gif, я не уверен, что произойдет с другими форматами файлов или файлами с другим хранилищем ColorModel - информация может быть потеряна (например, 4 цветовых канала к 3). Для упомянутых типов нам не нужен альфа-канал, даже если мы конвертируем из gif или jpg в png (это будет Color.WHITE).



СТАРОЕ РЕШЕНИЕ

Мне не понравился мой первый дизайн, и он не совсем работал так, как должен.

Поэтому я создал его с нуля. В итоге я получил небольшой конвертер для файлов sRGB. Вы можете конвертировать из png в jpg и наоборот (Edit: добавлена ​​поддержка gif). Если вы хотите обрабатывать другие типы, не стесняйтесь расширять это дальше. Вы можете более или менее добавить его таким же образом. Это может работать и для других типов файлов, но я их еще не тестировал. К счастью, похоже, что sRGB встречается довольно часто.

Tbh. Я понятия не имею, сколько комбинаций и вариантов (цветовые палитры, точность, качество, ч / б и т. Д. c.) Вы можете произвести или какие общие свойства они имеют.

Может быть, вам этого достаточно . Может быть нет. По крайней мере, для меня это было приятным упражнением.

Это решение отнюдь не идеальное. Результаты выглядели нормально - я sh. Преобразование типа файла сработало, и размер файла также меньше, чем png.

try {
    final String fileName = "dice.png";
    final BufferedImage inputImage = ImageIO.read(new FileInputStream(fileName));
    final boolean isSRGB = inputImage.getColorModel().getColorSpace().isCS_sRGB();
    final String outputFormat = "gif";
    if (!isSRGB) {
        throw new IllegalArgumentException("Please provide an image that supports sRGB.");
    }
    final WritableRaster raster = createRaster(inputImage);
    final ColorModel colorModel = createColorModel(inputImage);
    final BufferedImage outputImage = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
    final String outputFileName = fileName + "-converted." + outputFormat;
    final boolean writeResult = ImageIO.write(outputImage, outputFormat, new FileOutputStream(outputFileName));
    if (!writeResult) {
        throw new IllegalStateException("Could not convert file: " + fileName + " to format: " + outputFormat);
    }
    System.out.println(">> Created file: " + outputFileName);
} catch (Exception e) {
    e.printStackTrace();
}
@NotNull
public static ColorModel createColorModel(@NotNull BufferedImage bufferedImage) {
    Objects.requireNonNull(bufferedImage);
    final int type = bufferedImage.getType();
    boolean isAlphaPremultiplied = false;
    int transparency = Transparency.OPAQUE;
    if (type == BufferedImage.TYPE_3BYTE_BGR) {
        isAlphaPremultiplied = true;
    }
    return new ComponentColorModel(
            ColorModel.getRGBdefault().getColorSpace(),
            false, isAlphaPremultiplied, transparency,
            bufferedImage.getData().getDataBuffer().getDataType()
    );
}

@NotNull
public static WritableRaster createRaster(@NotNull BufferedImage bufferedImage) {
    Objects.requireNonNull(bufferedImage);
    final int type = bufferedImage.getType();
    final int width = bufferedImage.getWidth();
    final int height = bufferedImage.getHeight();
    final int pixelStride = 3;
    int[] offset = new int[]{0, 1, 2};
    DataBufferByte dataBufferByte;

    if (type == BufferedImage.TYPE_4BYTE_ABGR || type == BufferedImage.TYPE_BYTE_INDEXED) {
        int dataIndex = 0;
        final byte[] data = new byte[height * width * pixelStride];
        final int bitmask = 0xff;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                final int rgb = bufferedImage.getRGB(x, y);
                final int blue = bitmask & rgb;
                final int green = bitmask & (rgb >> 8);
                final int red = bitmask & (rgb >> 16);
                if (rgb == 0) {
                    data[dataIndex++] = (byte) bitmask;
                    data[dataIndex++] = (byte) bitmask;
                    data[dataIndex++] = (byte) bitmask;
                } else {
                    data[dataIndex++] = (byte) red;
                    data[dataIndex++] = (byte) green;
                    data[dataIndex++] = (byte) blue;
                }
            }
        }
        dataBufferByte = new DataBufferByte(data, data.length);
    } else if (type == BufferedImage.TYPE_3BYTE_BGR) {
        dataBufferByte = (DataBufferByte) bufferedImage.getRaster().getDataBuffer();
        offset = new int[]{2, 1, 0};
    } else {
        throw new IllegalArgumentException("Cannot create raster for unsupported image type.");
    }

    return Raster.createInterleavedRaster(
            dataBufferByte, width, height,
            pixelStride * width, pixelStride,
            offset,
            null
    );
}

Примеры преобразования изображений

EDIT : добавлена ​​поддержка gif.

...