【问题标题】:Spring MVC image uploading and ImageIO conversion fails on LinuxLinux 上 Spring MVC 图片上传和 ImageIO 转换失败
【发布时间】:2018-04-05 06:43:56
【问题描述】:

我正在开发一个用户可以上传图片的网站。

我为此创建了一个 Spring 控制器。当用户上传图像时,我想将其转换为 PNG,然后将其上传到 AWS S3 文件夹。在 Windows 上一切正常,但在 Linux 上使用java.lang.IllegalArgumentException: image == null! 进行 PNG 转换失败。

Windows 和 Linux 都安装了相同的 Oracle JDK,Java 版本为 1.8.0_161。

这些是服务器上可用的图像阅读器:

Oracle Corporation | 0.5 | Standard JPEG Image Reader
Oracle Corporation | 1.0 | Standard BMP Image Reader
Oracle Corporation | 1.0 | Standard WBMP Image Reader
Oracle Corporation | 1.0 | Standard GIF image reader
Oracle Corporation | 1.0 | Standard PNG image reader

我使用 JPG、PNG 和 GIF 文件对其进行了测试。他们都失败了。

确切的例外是:

018-04-05 06:23:57.414 ERROR 31434 --- [p-nio-80-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalArgumentException: image == null!] with root cause

java.lang.IllegalArgumentException: image == null!
        at javax.imageio.ImageTypeSpecifier.createFromRenderedImage(ImageTypeSpecifier.java:925)
        at javax.imageio.ImageIO.getWriter(ImageIO.java:1592)
        at javax.imageio.ImageIO.write(ImageIO.java:1578)
        at com.morethanheroic.restaurantapp.restaurant.service.logo.RestaurantLogoUploader.convertImageToPng(RestaurantLogoUploader.java:69)

当我禁用 PNG 转换并且只将原始图像上传到 S3 时,从 Linux 上传的图像的大小会稍大一些(大约 +30%)。正确的图片显然是从 Windows 上传的。

我发布了所有相关代码,因为我不确定巫婆步骤会导致问题。

这是我的控制器:

package com.morethanheroic.restaurantapp.restaurant.view.controller;

import com.morethanheroic.restaurantapp.restaurant.domain.RestaurantEntry;
import com.morethanheroic.restaurantapp.restaurant.service.RestaurantEntryFactory;
import com.morethanheroic.restaurantapp.restaurant.service.logo.RestaurantLogoUploader;
import com.morethanheroic.restaurantapp.restaurant.view.controller.exception.UnauthorizedAccessException;
import com.morethanheroic.restaurantapp.restaurant.view.service.file.FileConverter;
import com.morethanheroic.user.domain.UserEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/restaurant/{restaurantId}/logo")
@RequiredArgsConstructor
public class RestaurantLogoUploadController {

    private final RestaurantEntryFactory restaurantEntryFactory;
    private final RestaurantLogoUploader restaurantLogoUploader;
    private final FileConverter fileConverter;

    @PostMapping("/upload")
    public void uploadFile(final UserEntity userEntity, @PathVariable final int restaurantId,
                           @RequestPart("logo") final MultipartFile logo) {
        final RestaurantEntry restaurantEntry = restaurantEntryFactory.getRestaurant(restaurantId);

        if (!restaurantEntry.isOwner(userEntity)) {
            throw new UnauthorizedAccessException();
        }

        restaurantLogoUploader.uploadLogo(restaurantEntry, fileConverter.convertMultiPartToFile(logo));
    }
}

文件转换器:

package com.morethanheroic.restaurantapp.restaurant.view.service.file;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

@Slf4j
@Service
public class FileConverter {

    public File convertMultiPartToFile(final MultipartFile multipartFile) {
        final File result = new File(multipartFile.getOriginalFilename());

        try (final FileOutputStream fos = new FileOutputStream(result)) {
            fos.write(multipartFile.getBytes());
        } catch (IOException e) {
            log.error("Unable to convert MultiPart to File!", e);
        }

        return result;
    }
}

这是发生转换的实际图片上传器。

package com.morethanheroic.restaurantapp.restaurant.service.logo;

import com.amazonaws.services.s3.AmazonS3;
import com.morethanheroic.restaurantapp.restaurant.domain.RestaurantEntry;
import com.morethanheroic.restaurantapp.restaurant.service.logo.configuration.RestaurantLogoProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

@Slf4j
@Service
public class RestaurantLogoUploader {

    private final AmazonS3 amazonS3;
    private final RestaurantLogoProperties restaurantLogoProperties;
    private final RestaurantLogoNameFactory restaurantLogoNameFactory;

    public RestaurantLogoUploader(@Qualifier("amazonLogoUploaderS3Client") final AmazonS3 amazonS3,
                                  final RestaurantLogoProperties restaurantLogoProperties,
                                  final RestaurantLogoNameFactory restaurantLogoNameFactory) {
        this.amazonS3 = amazonS3;
        this.restaurantLogoProperties = restaurantLogoProperties;
        this.restaurantLogoNameFactory = restaurantLogoNameFactory;
    }

    public void uploadLogo(final RestaurantEntry restaurant, final File restaurantLogo) {
        final String logoName = restaurantLogoNameFactory.buildName(restaurant);

        amazonS3.putObject(restaurantLogoProperties.getBucketName(), logoName, convertImageToPng(restaurantLogo));
    }

    private File convertImageToPng(final File inputFile) {
        log.info("Converting file " + inputFile.getAbsolutePath() + " to png.");

        try {
            final BufferedImage bufferedImage = ImageIO.read(inputFile);
            final ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();

            ImageIO.write(bufferedImage, "png", byteArrayOut);

            final byte[] resultingBytes = byteArrayOut.toByteArray();

            final File result = new File(inputFile.getName());
            FileOutputStream fos = new FileOutputStream(result);
            fos.write(resultingBytes);
            fos.close();

            return result;
        } catch (IOException e) {
            throw new RuntimeException("Unable to convert image.");
        }
    }
}

【问题讨论】:

  • 日志说明了什么(“正在将文件...转换为 png。”)。 ImageIO.read 显然返回了 null,因为它可能会这样做。可能是文件名不对。
  • 在servlet环境中通常不需要处理Files。我通常会这样做:InputStream is = multipartFile.getInputStream(); BufferedImage bi = ImageIO.read(is); ImageIO.write(bi, "png", ...);.
  • 我会在上传到 Linux 服务器后开始查看文件(可能附加一个失败的文件)。如果ImageIO.read 返回null,则无法识别该文件。您的图像转换代码看起来大部分都不错(除了写入字节数组,然后写入文件,您可以直接写入文件)。
  • 我在帖子中添加了更多信息。 “当我禁用 PNG 转换并且只将原始图像上传到 S3 时,从 Linux 上传的图像的大小会稍大一些(大约 +30%)。正确的图像显然是从 Windows 上传的图像。”
  • AmazonS3 还提供了接受流的方法,因此可以完全避免中间保存到文件。

标签: java linux spring spring-boot javax.imageio


【解决方案1】:

Web 应用程序不应以 root(管理员权限)运行,因此不允许访问 /root 目录。在别处配置上传目录。

【讨论】:

  • 谢谢,你说得对。我会尽快纠正这个错误。
【解决方案2】:

错误是由 AWS API Gateway 引起的。将“multipart/form-data”列为二进制媒体类型后,一切都按预期工作。

【讨论】:

    猜你喜欢
    • 2010-10-05
    • 2018-09-19
    • 1970-01-01
    • 1970-01-01
    • 2014-10-25
    • 2012-12-02
    • 2016-04-11
    • 2014-07-22
    • 1970-01-01
    相关资源
    最近更新 更多