html์ ํ์ฑํด์ ์ธ๋ค์ผ ์ด๋ฏธ์ง๋ฅผ ์ถ์ถํด Amazon S3์ ์ ์ฅํด์ผ ํ๋ ์ผ์ด ์์์ต๋๋ค.
์ฌ์ด๋ ํ๋ก์ ํธ ์ธ ๋งํผ ์ด๋ฏธ์ง ์ฉ๋์ ์ค์ผ ์ ์์ผ๋ฉด S3 ๋น์ฉ๋ ์๋ผ๊ณ ํ๋ก ํธ ๋ ๋๋ง ์๋ ์ญ์ ๊ฐ์ ๋๊ฒ ๋ค๊ณ ์๊ฐ์ด ๋ค์๊ณ resize์ webp ์ด๋ฏธ์ง ํฌ๋งท์ ์ฌ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ์ต๋๋ค.
์ด๋ฏธ์ง ์ฝ๊ธฐ
URL ์ ๋ณด๋ฅผ ํตํด์ ์ด๋ฏธ์ง๋ฅผ ์ฝ์ด์์ผ ํ์๋๋ฐ Java์์ javax.imageio.ImageIO ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ฉด ์ฝ๊ฒ ์ด๋ฏธ์ง๋ฅผ ์ฝ๊ณ ์ธ ์ ์์์ต๋๋ค.
URL ํน์ ํ์ผ์ ์ฝ์ด์ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
BufferedImage image = ImageIO.read(new URL(์ด๋ฏธ์ง ์ฃผ์));
BufferedImage image = ImageIO.read(new File(์ด๋ฏธ์ง ์ ์ฅ ์์น));
- java.awt.image.BufferedImage๋ฅผ ํตํด์ ์ด๋ฏธ์ง์ width, height, RGB๋ฑ ๋ฉํ ๋ฐ์ดํฐ๋ฅผ ์ป์ ์ ์์ต๋๋ค.
**scrimage ๋ผ์ด๋ธ๋ฌ๋ฆฌ(์๋ฐ, ์ฝํ๋ฆฐ, ์ค์นผ๋ผ)๋ฅผ ํตํด์๋ ์ด๋ฏธ์ง๋ฅผ ์ฝ์ด์ฌ ์ ์์ต๋๋ค.**
ImmutableImage image = ImmutableImage.loader().fromFile(file);
๋ฉํ๋ฐ์ดํฐ๋ ์ฝ๊ฒ ์ป์ด์ฌ ์ ์์ต๋๋ค.
ImageMetadata meta = ImageMetadata.fromStream(stream);
Arrays.stream(meta.tags()).forEach(tag -> System.out.println(tag));
์ถ๋ ฅ
...
Tag{name='Compression Type', type=-3, rawValue='0', value='Baseline'}
Tag{name='Data Precision', type=0, rawValue='8', value='8 bits'}
Tag{name='Image Height', type=1, rawValue='405', value='405 pixels'}
Tag{name='Image Width', type=3, rawValue='594', value='594 pixels'}
Tag{name='Resolution Units', type=7, rawValue='1', value='inch'}
Tag{name='X Resolution', type=8, rawValue='300', value='300 dots'}
Tag{name='Y Resolution', type=10, rawValue='300', value='300 dots'}
Tag{name='Thumbnail Width Pixels', type=12, rawValue='0', value='0'}
Tag{name='Thumbnail Height Pixels', type=13, rawValue='0', value='0'}
Tag{name='Image Width', type=256, rawValue='4928', value='4928 pixels'}
Tag{name='Image Height', type=257, rawValue='3280', value='3280 pixels'}
Tag{name='Bits Per Sample', type=258, rawValue='8 8 8', value='8 8 8 bits/component/pixel'}
Tag{name='Photometric Interpretation', type=262, rawValue='2', value='RGB'}
Tag{name='Image Description', type=270, rawValue='during the Sky Bet Championship match between Middlesbrough and Wolverhampton Wanderers at Riverside Stadium on April 14, 2015 in Middlesbrough, England.', value='during the Sky Bet Championship match between Middlesbrough and Wolverhampton Wanderers at Riverside Stadium on April 14, 2015 in Middlesbrough, England.'}
Tag{name='Make', type=271, rawValue='NIKON CORPORATION', value='NIKON CORPORATION'}
Tag{name='Model', type=272, rawValue='NIKON D4S', value='NIKON D4S'}
Tag{name='Orientation', type=274, rawValue='1', value='Top, left side (Horizontal / normal)'}
Tag{name='Samples Per Pixel', type=277, rawValue='3', value='3 samples/pixel'}
Tag{name='X Resolution', type=282, rawValue='72', value='72 dots per inch'}
Tag{name='Y Resolution', type=283, rawValue='72', value='72 dots per inch'}
...
์ด๋ฏธ์ง ์ฌ์ด์ฆ ์กฐ์ ํ๊ธฐ
BufferedImage (PNG)
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
public class ReadWriteImage {
public static void main(String[] args) {
try {
URL url = new URL(์ด๋ฏธ์ง URL ์ฃผ์);
// read an image from url
BufferedImage image = ImageIO.read(url);
// resize image to 300x150
Image scaledImage = image.getScaledInstance(300, 150, Image.SCALE_DEFAULT);
// save the resize image aka thumbnail
ImageIO.write(
convertToBufferedImage(scaledImage),
"png",
new File(ํ์ผ ์ ์ฅ ์ฃผ์));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Done");
}
// convert Image to BufferedImagepublic static BufferedImage convertToBufferedImage(Image img) {
if (img instanceof BufferedImage) {
return (BufferedImage) img;
}
// Create a buffered image with transparency
BufferedImage bi = new BufferedImage(
img.getWidth(null), img.getHeight(null),
BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics2D = bi.createGraphics();
graphics2D.drawImage(img, 0, 0, null);
graphics2D.dispose();
return bi;
}
}
JPEG, JPG๋ฅผ ์์ ์ฝ๋๋ก ์ด๋ฏธ์ง ํ์ผ์ ์์ฑํ๋ ค๊ณ ์์ธ๋ ๋ฐ์ํ์ง ์๋๋ฐ ํ์ผ์ด ์์ฑ๋์ง ์๋ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
- PNG๋ ์ํ ์ฑ๋์ ์ง์ํ์ง๋ง JPEG ํ์ผ์ ์ํ ์ฑ๋์ ์ง์ํ์ง ์๋ ๊ฒ์ ์ธ์งํ์ง ๋ชปํ์ต๋๋ค.
- ์ํ์ฑ๋์ RGB 3๊ฐ์ ์ฑ๋ ์ธ์ ํธ์ง์ฉ ์ ๋ณด๋ฅผ ์ทจ๊ธํ๋ ๋ณด์กฐ์ฑ๋์ ๋งํฉ๋๋ค.
BufferedImage.TYPE_INT_ARGB// ์ํ ์ฑ๋ O
BufferedImage.TYPE_3BYTE_BGR// ์ํ ์ฑ๋ X
- TYPE_3BYTE_BGR๋ก ๋ณ๊ฒฝํด์ฃผ์๋ฉด ์ ์์ ์ผ๋ก JEPG, JPG ์ด๋ฏธ์ง ํ์ผ์ด ์์ฑํ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
scrimage ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- resize ๋ฉ์๋๋ ์กด์ฌํ์ง๋ง ์ด๋ฏธ์ง๋ฅผ ์ถ์ํ์๋ ์ฉ๋๋ก ์ฌ์ฉํ์๋ ๋ถ๋ค์ scale ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์๋ฉด ๋ฉ๋๋ค.
image.scaleToWidth(400)// keeps aspect ratio
image.scaleToHeight(200)// keeps aspect ratio
image.scaleTo(400, 400)
WebP
WebP ํ์ผ์ ์ ์ฅํ ๋ ์์ถ ๋ฐฉ์(๋ฌด์์ค ์์ถ, ์์ค ์์ถ)์ ์ ํํ ์ ์์ผ๋ฏ๋ก ๋ฐ์ดํฐ ์์ค ์์ด ๋๋ ์ค์ํ ์ ๋ณด๋ฅผ ์์คํ์ง ์๊ณ ์ด๋ฏธ์ง๋ฅผ ์์ถํ ์ ์์ต๋๋ค. (์ฐธ์กฐ)
- WebP ๋ฌด์์ค ์ด๋ฏธ์ง๋ PNG์ ๋นํด ํฌ๊ธฐ๊ฐ 26% ๋ ์์ผ๋ฉฐ, JPEG ์ด๋ฏธ์ง๋ณด๋ค 25~34% ๋ ์์ต๋๋ค.
- ์์ค RGB ์์ถ์ด ํ์ฉ๋๋ ๊ฒฝ์ฐ ์์ค์ด ์๋ WebP๋ ํฌ๋ช ๋๋ ์ง์ํ์ฌ ์ผ๋ฐ์ ์ผ๋ก PNG์ ๋นํด 3๋ฐฐ ์์ ํ์ผ ํฌ๊ธฐ๋ฅผ ์ ๊ณตํฉ๋๋ค.
scrimage ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด์ WebP๋ก ๋ณํํ๊ธฐ
// ๋ฒ์ ์ ๋ณด: <https://sksamuel.github.io/scrimage/changelog/>
implementation("com.sksamuel.scrimage:scrimage-core:4.1.3")
implementation("com.sksamuel.scrimage:scrimage-webp:4.1.3")// WebP๋ฅผ ์ํด ์ถ๊ฐ
// ์ฝ๊ธฐ
ImmutableImage.loader().fromFile(new File("someimage.webp"))
// ์ฐ๊ธฐ
myimage.output(WebpWriter.MAX_LOSSLESS_COMPRESSION,"output.webp");
Gif๋ ์ฝ๊ณ ์ธ ์ ์๋ค.
// ์ฝ๊ธฐ
AnimatedGifReader.read(ImageSource.of(File("animated.gif"));
// ์ฐ๊ธฐ
animatedGif.bytes(Gif2WebpWriter.DEFAULT);
animatedGif.output(Gif2WebpWriter.DEFAULT, "output.webp");
ํ๋ก์ ํธ ์ ์ฉ ์ฝ๋ (์ฝํ๋ฆฐ)
URL ํ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์์ผ ํ์๊ธฐ ๋๋ฌธ์ ImageIO๋ฅผ ํตํด์ BufferedImage๋ฅผ ๊ฐ์ ธ์จ ๋ค์์ width๋ฅผ ๊ธฐ์ค์ผ๋ก ์ฌ์ด์ฆ๋ฅผ ์กฐ์ ํ๊ณ .webp๋ก ๋ณํํ์ต๋๋ค.
fun imageResizeAndConvertWebp(image: BufferedImage): String {
val immutableImage = getImmutableImage(image)
.scaleToWidth(TARGET_WIDTH)
.output(WebpWriter.DEFAULT, File("$path/${UUID.randomUUID()}$WEBP_SUFFIX"))
}
private fun getImmutableImage(image: BufferedImage): ImmutableImage =
ImmutableImage.create(
image.width,
image.height,
PixelFactory.getPixelArrayFromImage(image),
BufferedImage.TYPE_3BYTE_BGR
)
ImmutableImage.create ๋ฉ์๋์์ Pixel ํ์ ์ ๋ฐฐ์ด์ ๋ฐ๊ฒ ๋๋๋ฐ stackoverflow ์๋ ์ฝ๋๋ฅผ ์ ๊ณตํด์ฃผ์ ์ ์์ฑํ๊ฑธ ์ด์ฉํด์ ์์ฑํ์ต๋๋ค.
private static int[][] convertTo2DWithoutUsingGetRGB(BufferedImage image) {
final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
final int width = image.getWidth();
final int height = image.getHeight();
final boolean hasAlphaChannel = image.getAlphaRaster() != null;
int[][] result = new int[height][width];
if (hasAlphaChannel) {
final int pixelLength = 4;
for (int pixel = 0, row = 0, col = 0; pixel + 3 < pixels.length; pixel += pixelLength) {
int argb = 0;
argb += (((int) pixels[pixel] & 0xff) << 24);// alpha
argb += ((int) pixels[pixel + 1] & 0xff);// blue
argb += (((int) pixels[pixel + 2] & 0xff) << 8);// green
argb += (((int) pixels[pixel + 3] & 0xff) << 16);// red
result[row][col] = argb;
col++;
if (col == width) {
col = 0;
row++;
}
}
} else {
final int pixelLength = 3;
for (int pixel = 0, row = 0, col = 0; pixel + 2 < pixels.length; pixel += pixelLength) {
int argb = 0;
argb += -16777216;// 255 alpha
argb += ((int) pixels[pixel] & 0xff);// blue
argb += (((int) pixels[pixel + 1] & 0xff) << 8);// green
argb += (((int) pixels[pixel + 2] & 0xff) << 16);// red
result[row][col] = argb;
col++;
if (col == width) {
col = 0;
row++;
}
}
}
return result;
}
- ๊ธฐ์กด์ ์ค์ฒฉ ๋ฐ๋ณต๋ฌธ ๋ณด๋ค 10๋ฐฐ ์ด์ ๋น ๋ฅธ ์ฝ๋
๊ฒฐ๊ณผ
JPEG
84.36kB -> 39.26kB(Resize Image) -> 23.02kB (Convert WebP) 72.71% ๊ฐ์
PNG (์์ค ์์ถ)
920.3 kB -> 366.94kB(Resize Image) -> 22.4kB(Convert WebP) 97.6% ๊ฐ์
๐ reference
'๊ฐ๋ฐ > BACKEND' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
PK๋ฅผ ์ด๋ป๊ฒ ์ค์ ํ๋ ๊ฒ์ด ์ข์๊น? ๐ค (0) | 2024.07.10 |
---|---|
[Query dsl]hibernate.query.SemanticException : Could not interpret path expression (0) | 2024.07.10 |
[์๋ฌ ํด๊ฒฐ] cannot resolve class or package 'cj' (2) | 2024.07.10 |
spring batch์์ DataSource ๋ถ๋ฆฌ (0) | 2024.07.10 |
Spring Batch JpaItemWriter์์ List<Entity> ์ฒ๋ฆฌํ๊ธฐ (0) | 2024.07.10 |