-
Notifications
You must be signed in to change notification settings - Fork 0
Image Compress
flutter에서 기본적으로 지원하는 이미지 형식은 jpeg / png / webp 형식이 존재 합니다.
jpeg - 이미지 품질과 파일 크기 간의 적절한 균형을 제공하기 때문에 일반적으로 세부 묘사가 많은 사진에 적합한 상태, 가장 많이 사용됩니다.
png - 아이콘이나 로고와 같이 투명도가 있는 이미지에 적합한형태
webp - jpeg / png 보다 더 나은 압축을 제공하는 최신 이미지 형태로 이미지 품질을 희생시키지 않고도 파일 사이즈를 조절 가능 한 형태입니다. 현재 여러 웹사이트들에서 가장 많이 사용되며 각광받는 형태입니다. (macOS에는 적용이 안되기때문에 mac관련 앱을 만드실경우에는 사용이 불가합니다.)
이 이외에도 bmp - 비트맵처럼 디지털 그림을 저장하는데 쓰입니다. bit들의 map이며, 압축을 전혀 하지 않기 때문에 사이즈가 매우 큽니다. 하지만 디코딩을 해야할 부분이 없어서 속도가 빠르다는 장점이 있습니다. 그래서 윈도우 프로그래밍이나 운영체제단에서 많이 사용됩니다. 처리 속도가 빠르다는 장점이 있지만 용량이 너무 큰부분이 문제입니다.
svg - 2차원 벡터 그래픽을 표현을 위한 XML 기반의 파일입니다. XML텍스트 파일로 정의 되어 검색화/목록화/스크립트화가 가능하며, 용량이 기본적으로 작고 해상도와 관계없이 빠르고 깔끔한 이미지 표현이 가능합니다. flutter자체 지원을 하고 있지는 않기 때문에 외부 라이브러리 사용이 필수입니다.
이미지 압축은 저장과 전송 비용을 줄이기 위해 사용합니다.
압축의 방식은 여러가지가 있지만 가장 많이 사용되는 대표적인 예시가 무손실 압축 / 손실 압축 입니다.
- 원본 이미지 품질을 유지하지만 파일 크기가 크게 줄어들지 않습니다.
- 파일 사이즈를 줄이기 위해 이미지 품질을 깎아서 낮춤
모바일에서 유저가 사진을 전송하거나 / 프로필 이미지를 등록 / 포스팅을 작성하게 될 경우 이미지 압축을 사용하지 않으면 데이터 쌓이는 양이 어마어마 해지며 초기에 관리를 하지 못하면 추후에 이미지를 불러와야 하는 부분에서도 다른 유저에게까지 엄청난 로딩 시간을 주어 사용자에게 앱을 사용함에 있어 불쾌함을 주게 됩니다.
그래서 어떤방식을 사용 하는게 가장 좋을지에 대한 고민을 계속 하던 와중에 https://www.youtube.com/watch?v=Qe83Ss3zh74 이 영상을 보게 되었고, 이 후 webp형식이 가장 좋은 대안이라고 생각하여 시도했습니다.
현재 flutter 에서 이미지 압축을 위해 많이 사용되는 라이브러리는 2종류가 있습니다.
https://pub.dev/packages/flutter_image_compress
https://pub.dev/packages/image
대게 2번째 패키지를 많이 사용하고 있으나 image package는 webp형식을 지원하지 않아서 image_compress package를 사용해서 적용해봤습니다.
dependencies:
flutter_image_compress: ^2.3.0
패키지만 추가 해주시면 되고 이 외에 다른 설정은 필요 없습니다.
이제 내가 사용하는 camera나 gallery를 가져오는 패키지에 대한 이해도가 필요합니다.
ImagePicker을 사용하게 되는 경우 기본적으로 사진을 촬영하거나 갤러리에서 선택 된 이미지를 가져오는 경우 XFile형태로 가져오게 됩니다.
기본적으로 image_compress는 File형태를 변환 작업을 거치기 때문에 이미지를 수정 하실때 XFile을 File로 변환하는 코드를 삽입해주시면 됩니다.
이미지 파일을 변환 시키는 예제 함수
임시 디렉토리를 얻어서 사용하는 이유에 대한 추가 설명을 하겠습니다.
- 임시 디렉토리는 시스템에서 임시 파일 저장을 위해 사용되는 공간이며, 일시적인 데이터 저장에 있어 가장 최적화 되어 있기 때문에 저장공간을 불필요하게 차지하지 않습니다.
- 임시 디렉토리 파일은 시스템에 의해 주기적으로 삭제 처리 되며, 장기적으로 저장공간의 문제를 발생시키지 않습니다.
- 임시 디렉토리 파일은 일반적으로 빠른 읽기/쓰기 능력에 특화되어 있어서 작업 성능이 향상됩니다.
- 임시 디렉토리는 앱의 샌드박스 내에 위치해 있으며 별도의 저장공간 권한 요청이 필요 없습니다.
- 시간으로 파일명을 지정한 이유는 시간으로 저장하게 되었을 경우 각 시간별로 고유한 값을 가지기 때문에 파일 이름 충돌을 방지하기 위해 사용했습니다.
부가적으로 개인의 필요한 여하에 따라 퀄리티 , 사이즈 등은 개별적으로 조정 하시면 됩니다.
PhotoManager은 XFile형태가 아닌 그대로 File형태를 반환하게 됩니다. 그래서 추가적인 변환 로직이 필요없이 가능합니다.
파일 형태 이외에 다른 부분들이 동일 하기 때문에 추가적인 설명은 생략 하겠습니다 아래 복사가 가능한 코드는 따로 첨부 해두겠습니다.
추가적으로 궁금하신 내용이 있다면 언제든지 문의 해주세요
ImagePicker를 사용 할 때
Future<File?> resizeAndConvertToWebP(
XFile imageFile, int width, int height) async {
try {
// 임시 디렉토리 얻기
final directory = await getTemporaryDirectory();
final targetPath =
'${directory.path}/resized_${DateTime.now().millisecondsSinceEpoch}.webp';
// XFile을 File로 변환
File file = File(imageFile.path);
// 이미지 압축 및 WebP로 변환
final result = await FlutterImageCompress.compressAndGetFile(
file.absolute.path,
targetPath,
format: CompressFormat.webp,
quality: 80,
minWidth: width,
minHeight: height,
);
if (result == null) {
throw Exception('Failed to compress and convert image');
}
return File(result.path);
} catch (e) {
talker.error('Error resizing and converting image: $e');
return null;
}
}
PhotoManager을 사용 할 때
Future<File> compressToWebP(File file) async {
final tempDir = await getTemporaryDirectory();
final targetPath =
'${tempDir.path}/temp_${DateTime.now().millisecondsSinceEpoch}.webp';
final compressedFile = await FlutterImageCompress.compressAndGetFile(
file.absolute.path,
targetPath,
quality: 80,
format: CompressFormat.webp,
minWidth: 800,
minHeight: 800,
);
if (compressedFile == null) {
throw Exception('Failed to compress image');
}
return File(compressedFile.path);
}
IOS시뮬레이터안에 있는 예제 이미지의 사이즈는 2 - 4MB 사이 입니다. 이때 리사이징을 하게 되면 100 - 300KB사이로 줄어들게 됩니다.
간단하게 설명을 하자면 3MB짜리 파일을 한개 가져올때 걸리는 시간이 1초라고 예시를 하면 1초에 3000KB를 가져오게 되며 리사이징 된 파일 1개의 값을 200KB로 설정한다면 리사이징을 시도 했을 경우에 15개를 가져오게 됩니다.
물론 서버 성능 / 네트워크 지연에 따라 달라질수 있으며 단순한 예시입니다.
유저가 이미지를 선택해서 가져오고 버튼을 클릭했을때 바로 업로드 되는 로직을 구성하는 예시를 들어보겠습니다.
이렇게 될 경우
이런 형태로 처리하게 되는데 Dart는 멀티 스레드 기능이 아닌 싱글 스레드 방식입니다. Isolate방식을 사용해서 따로 빼서 처리 할수 있지만 이렇게 하게되면 패키지를 사용할수 없고 순수 dart 코드로 파일 압축을 진행 해야 합니다. 물론 불가능한 일은 아니지만 너무 어렵고 해야할게 많아지기 때문에 저는 다른 방법을 선택 했습니다.
다이얼로그 형태나 / 프리뷰 페이지를 만들어서 먼저 리사이징 처리만 하게 시킵니다. 이렇게 되면 소요시간이 현저하게 짧아지며, 프로그레스바 처리로 로딩상태를 유저에게 보여주지 않고도 해결 가능합니다.
서버와 통신 리사이징을 최대한 빠르게 구현하는게 목표이나, 가진 기술의 한계가 있다면 너무 많은 부담을 유저에게 전달하지 않으면서 해결하기 위해서는 이 방법이 가장 나은 선택입니다.
너무 어려운 부분을 고민하며 해결해 나가는것도 엔지니어의 개인 능력이라고 생각은 하지만 최선은 아니지만 차선이 존재한다면 우선 차선을 선택하고 꾸준히 역량을 키워 나가며 추가적인 보수를 해 나가는것도 좋은 방법입니다.