我有一个图像上传servlet,它通过HTTP POST接收上传的图像,是高分辨率图像,大小从5 MB到75 MB不等。从请求输入流中读取图像数据并将其保存到本地磁盘上。我正在寻找一种有效的机制来从请求输入流生成不同大小(4-5种不同大小,其中最大的是webimage - 1024x768)并行(或部分顺序,如果不是完全并行)的缩略图,同时将流保存到磁盘作为原始上传文件。
到目前为止我能想到的是 -
有人可以建议一种更有效的方法吗?最理想的方法是同步执行此操作,但如果非常有效,则异步也很好。
这方面的任何帮助都将在Java中得到很好的理解。
答案 0 :(得分:0)
这是一个非常有趣的问题,因为它有许多优化点。
关于生成较小图像然后生成缩略图的想法可能是一个很好的但我要说的第一件事就是如果你有75MB的图像那么它显然远大于1024x768 - 很可能是它的一些倍数,在这种情况下,您要确保使用SCALE_FAST(Image)缩放图像。您想要实现的是缩放通过丢弃像素而不是尝试做任何更好看(并且更昂贵)的区域平均来缩小图像。您甚至可以通过抓取图像的int []并对每个第N个元素进行采样来为新图像创建新的int [],并按某种因素按比例缩小,以使其更快。
此时您将拥有一个较小的图像,比如2000年大约2000.然后您可以拍摄该图像并使用更好的内容来缩放它,以查找SCALE_SMOOTH等实际缩略图。
我会说你应该不将它写入磁盘(如果可能的话)(无论如何处理)。如果你可以在内存中进行操作,它会快得多,而且在有并行性的情况下,这是非常重要的。除非您的服务器运行SSD,然后同时运行两个磁盘操作(例如,其中两个图像同时重新调整或一个图像重新调整为两个不同的大小)将强制您的磁盘捶打(因为主轴一次只能读取一个流)。然后,您将受到寻找时间的摆布,您很快就会发现序列化所有操作的速度远远快于一次执行多次操作。
我想说它们在内存中重新缩放然后将它们(同步)写入ArrayList,然后让另一个线程按顺序读取这些图像并存储它们。如果你不确定我的意思,那么请看看我对另一个问题的回答:
Producer Consumer solution in Java
通过这种方式,您可以并行处理其有用的(CPU操作)并按顺序写入文件(避免颠簸)。
话虽如此,你需要问问自己,并行化是否会让你受益匪浅。您的服务器是否有多个CPU /核心?如果没有,那么这一切都没有实际意义,你应该不打扰任何东西,因为它只会浪费你的时间。
除此之外,如果您希望立即上传大量这些图像,那么您可能不需要对每个图像的处理进行并行处理,因为您将最终处理多个Web服务器线程,所有这些线程都处理一个图像。无论如何,时间和性能将为您提供更好的CPU利用率。例如,如果您希望在任何时候都会有4个图像被不断上传,那么这将使用4个核心就好了而无需进一步并行化。
最后一个注意事项是,当您重新缩放图像时,一旦拥有了中间图像,您可以将上一个图像设置为null以便于垃圾收集,这意味着当您生成缩略图时,您将只在内存中拥有中间图像,而不是原来的大尺寸。
答案 1 :(得分:0)
让我看看我是否做对了,
您有一个大图像,并希望同时对其执行不同的操作。某些操作涉及磁盘IO。
选项1, 启动1个线程将原始高分辨率图像保存到磁盘。与其他操作相比,这将花费很长时间,因为磁盘写入速度很慢。 启动其他线程以创建所需大小的缩略图。您需要调整原始图像的大小。我相信这可以通过克隆原始图像的字节来完成(在java中我假设BufferedImage)。然后,您可以根据所需的大小调整克隆的大小。与写入磁盘相比,调整大小操作更快。
如果每个缩略图有1个主题,则可以使用这些主题将缩略图保存到磁盘。问题是您将完成缩略图的快速操作,并且所有这些线程几乎会立即写入磁盘。这里的问题是它们可能被发送到不同的磁盘位置,而不是被分组在磁盘上的相同物理区域(位置问题)。结果是,磁盘写入将比不并行执行更慢,因为磁盘必须寻找新位置并写入一些数据,然后CPU执行上下文切换并采用另一个线程写入另一个部分磁盘(所以另一个寻求)等等。所以这个想法很慢。
注意:使用具有线程池的ExecutorService,而不是单个线程。在我的例子中,我每个缩略图使用1个线程,因为它更容易解释。
选项2, 另一种方法是指定一个线程来执行磁盘写入,还有一些其他工作线程来调整大小。将所有thmubnails缓存到一个列表中,写入磁盘的线程将从中逐个接收并写出来。
选项3, 最后,如果你有多个磁盘,你可以给每个线程一个磁盘写入,然后所有写入将并行(或多或少)。
如果您有RAID,写入速度会更快,但速度不如我刚才提到的那么快,因为文件不是并行写入的。 RAID并行化写入同一文件的部分(一次到不同的磁盘)。