청크 데이터(이진 데이터)를 그래도 보내도 되지만 압축하면 전송 효율이 좋아지기 때문에 압축 라이브러인 pako(파코)를 사용하여 압축하여 전송할 수 있습니다.
Blob(Binary Large Object)를 압축
pako (https://github.com/nodeca/pako)
pako(파코)는 Zlib 라이브러리의 JavaScript 구현체입니다.

1. pako(파코) 라이브러리를 CDN으로 가져옵니다.
<script src="https://cdnjs.cloudflare.com/ajax/libs/pako/2.1.0/pako.min.js"></script>
pako.gzip()은 Blob 객체를 직접 입력받을 수 없기 때문에 Uint8Array 같은 이진 배열(TypedArray) 형태로 데이터를 변환해서 사용해야 합니다.
ArrayBuffer (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)
ArrayBuffer는 원시 바이너리 데이터 버퍼 객체(고정된 크기의 연속된 메모리 바이트를 직접 할당받는 객체)입니다. (바이트 배열)

Uint8Array (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array)
Uint8Array는 부호 없는 8비트 정수의 배열(0부터 255 사이의 정수 배열) 객체입니다.

변환하는 방법
Blob → ArrayBuffer → Uint8Array
// Blob
const blob = new Blob(["Hello"], { type: 'text/plain' });
// Blob를 ArrayBuffer로 변환
const arrayBuffer = await blob.arrayBuffer();
// ArrayBuffer를 Uint8Array로 변환
const uint8Array = new Uint8Array(arrayBuffer);
Uint8Array → ArrayBuffer, Uint8Array → Blob
// Uint8Array "Hello"
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]);
// Uint8Array를 ArrayBuffer로 변환
const arrayBuffer = uint8Array.buffer;
// Uint8Array를 Blob으로 변환
const blob = new Blob([uint8Array], { type: 'application/octet-stream' });
2. 클라이언트에서 pako.gzip()를 사용하여 청크 데이터를 압축합니다.
// Blob을 ArrayBuffer로 변환
const arrayBuffer = await chunkData.arrayBuffer();
// ArrayBuffer를 Uint8Array로 변환
const uint8Array = new Uint8Array(arrayBuffer);
// pako로 압축
let compressedData = pako.gzip(uint8Array);
console.log(chunkData);
console.log(chunkData.size);
console.log(compressedData);
console.log(compressedData.length);

10240Byte가 8858Byte로 압축되는 것을 확인할 수 있습니다.
3. 전송을 위해 압축된 데이터를 다시 Blob으로 변환하고 전송합니다.
// 압축된 데이터를 다시 Blob으로 변환
compressedData = new Blob([compressedData], { type: 'application/gzip' });
console.log(compressedData);
console.log(compressedData.size);
chunkData = compressedData;
무결성 검증
데이터가 전송 중에 변하지 않았는지 검증하기 위해서 무결성 확인(Checksum) 용도로 단방향 해시 함수를 사용합니다.
암호화 JavaScript 라이브러리 (https://github.com/brix/crypto-js)

4. 암호화 라이브러리를 CDN으로 가져옵니다.
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js"></script>
5. 청크 데이터를 기반으로 단방향 해시 함수를 사용하여 무결성 검증을 위한 코드를 생성합니다.
// Blob을 ArrayBuffer로 변환
const blobBuffer = await chunkData.arrayBuffer();
// 단방향 해시 함수(MessageDigest) 사용, 리턴 값은 ArrayBuffer 객체
const hashBuffer = await crypto.subtle.digest('SHA-256', blobBuffer);
// 배열로 변환
const hashArray = Array.from(new Uint8Array(hashBuffer));
// 16진수 2자리 문자로 변환
verificationCode = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
console.log(verificationCode);

6. 서버에서 전송된 파일(청크 데이터)을 읽습니다.
// 청크 파일 읽기
InputStream chunkFileInputStream = chunkFile.getInputStream();
byte[] buffer = new byte[1024]; // 버퍼 크기 설정
int bytesRead = -1;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
while ((bytesRead = chunkFileInputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
}
byte[] compressedData = byteArrayOutputStream.toByteArray();
서버에서 전송된 청크 데이터로 단방향 해시 함수를 사용하여 무결성 검증을 위한 코드를 생성하고 클라이언트에 전달 받은 무결성 코드와 비교하여 검증합니다.
// 해시 코드
String hashCode = encryptSHA256(compressedData);
// 무결성 검증
if (verificationCode.equals(hashCode)) {
:
}
java.security의 MessageDigest를 사용하여 데이터를 해시한 후 16진수 문자열로 변환합니다.
private String encryptSHA256(byte[] bytes) {
String ciphertext = "";
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] encodedHash = md.digest(bytes);
StringBuilder builder = new StringBuilder();
for (byte b : encodedHash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
builder.append('0');
}
builder.append(hex);
}
ciphertext = builder.toString();
} catch (NoSuchAlgorithmException e) {
ciphertext = "";
}
return ciphertext;
}
7. 서버에서 Gzip를 사용하여 압축된 청크 데이터를 해제한 후 기존 파일에 이어서 저장합니다.
// 파일 쓰기 스트림
FileOutputStream fos = new FileOutputStream(tempFile, append);
// Gzip으로 압축 해제 후 저장
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedData);
GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream);
while ((bytesRead = gzipInputStream.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
'Spring > 대용량 파일 전송' 카테고리의 다른 글
| [7] 대용량 파일 전송 시스템 개발 - 파일 확장자 변조 검증, Tika (0) | 2026.08.23 |
|---|---|
| [6] 대용량 파일 전송 시스템 개발 - 암호화 전송, RSA + AES (0) | 2026.08.23 |
| [4] 대용량 파일 전송 시스템 개발 - 대용량 파일 업로드, Blob (0) | 2026.08.20 |
| [3] 대용량 파일 전송 시스템 개발 - 카테고리별 파일 업로드 설정 관리 전체 소스 (0) | 2026.08.20 |
| [2] 대용량 파일 전송 시스템 개발 - 개발 환경, 공통 부분 소스 (0) | 2026.08.19 |