메타데이터 API
Sharp는 이미지 메타데이터 정보를 읽고, 수정하고, 쓸 수 있는 풍부한 메타데이터 작업 기능을 제공합니다.
메타데이터 읽기
기본 사용법
javascript
import sharp from 'sharp';
// 이미지 메타데이터 읽기
const metadata = await sharp('input.jpg').metadata();
console.log(metadata);메타데이터 객체 구조
javascript
{
format: 'jpeg', // 이미지 형식
width: 1920, // 너비
height: 1080, // 높이
space: 'srgb', // 색상 공간
channels: 3, // 채널 수
depth: 'uchar', // 비트 깊이
density: 72, // 해상도
hasProfile: false, // 색상 프로파일 존재 여부
hasAlpha: false, // 투명도 채널 존재 여부
isOpaque: true, // 불투명 여부
orientation: 1, // EXIF 방향
exif: { ... }, // EXIF 데이터
icc: { ... }, // ICC 프로파일
iptc: { ... }, // IPTC 데이터
xmp: { ... }, // XMP 데이터
tifftagPhotoshop: { ... } // Photoshop TIFF 태그
}EXIF 데이터
EXIF 읽기
javascript
const metadata = await sharp('input.jpg').metadata();
if (metadata.exif) {
console.log('EXIF 데이터:', metadata.exif);
// EXIF 데이터 파싱
const exif = sharp.exif(metadata.exif);
console.log('촬영 시간:', exif.DateTime);
console.log('카메라 모델:', exif.Model);
console.log('ISO:', exif.ISOSpeedRatings);
}EXIF 쓰기
javascript
// EXIF 데이터 생성
const exif = sharp.exif({
IFD0: {
ImageDescription: 'Sharp로 처리된 이미지',
Copyright: '© 2024'
},
IFD1: {
Orientation: 1
},
EXIF: {
DateTimeOriginal: new Date().toISOString(),
UserComment: 'Sharp로 처리'
}
});
// EXIF 데이터 쓰기
await sharp('input.jpg')
.withMetadata({ exif })
.jpeg()
.toFile('output.jpg');ICC 색상 프로파일
ICC 프로파일 읽기
javascript
const metadata = await sharp('input.jpg').metadata();
if (metadata.icc) {
console.log('ICC 프로파일:', metadata.icc);
}ICC 프로파일 임베드
javascript
// sRGB 프로파일 임베드
await sharp('input.jpg')
.withMetadata({ icc: 'srgb' })
.jpeg()
.toFile('output.jpg');
// 사용자 정의 ICC 프로파일 임베드
const iccProfile = fs.readFileSync('custom.icc');
await sharp('input.jpg')
.withMetadata({ icc: iccProfile })
.jpeg()
.toFile('output.jpg');IPTC 데이터
IPTC 읽기
javascript
const metadata = await sharp('input.jpg').metadata();
if (metadata.iptc) {
console.log('IPTC 데이터:', metadata.iptc);
}IPTC 쓰기
javascript
const iptc = {
'2:05': 'Object Name',
'2:15': 'Category',
'2:25': 'Keywords',
'2:55': 'Date Created',
'2:80': 'By-line',
'2:116': 'Copyright'
};
await sharp('input.jpg')
.withMetadata({ iptc })
.jpeg()
.toFile('output.jpg');XMP 데이터
XMP 읽기
javascript
const metadata = await sharp('input.jpg').metadata();
if (metadata.xmp) {
console.log('XMP 데이터:', metadata.xmp);
}XMP 쓰기
javascript
const xmp = `
<x:xmpmeta xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title>Sharp로 처리된 이미지</dc:title>
<dc:creator>Sharp</dc:creator>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
`;
await sharp('input.jpg')
.withMetadata({ xmp })
.jpeg()
.toFile('output.jpg');방향 정보
방향 읽기
javascript
const metadata = await sharp('input.jpg').metadata();
console.log('이미지 방향:', metadata.orientation);자동 회전
javascript
// EXIF 방향에 따라 자동 회전
await sharp('input.jpg')
.rotate() // 자동 회전
.jpeg()
.toFile('output.jpg');해상도 정보
해상도 읽기
javascript
const metadata = await sharp('input.jpg').metadata();
console.log('해상도:', metadata.density);해상도 설정
javascript
await sharp('input.jpg')
.withMetadata({ density: 300 })
.jpeg()
.toFile('output.jpg');메타데이터 보존
모든 메타데이터 보존
javascript
// 모든 기존 메타데이터 보존
await sharp('input.jpg')
.resize(800, 600)
.withMetadata()
.jpeg()
.toFile('output.jpg');선택적 보존
javascript
// EXIF 데이터만 보존
await sharp('input.jpg')
.resize(800, 600)
.withMetadata({ exif: true })
.jpeg()
.toFile('output.jpg');
// EXIF 및 ICC 보존
await sharp('input.jpg')
.resize(800, 600)
.withMetadata({ exif: true, icc: true })
.jpeg()
.toFile('output.jpg');메타데이터 제거
모든 메타데이터 제거
javascript
// 모든 메타데이터 제거
await sharp('input.jpg')
.resize(800, 600)
.jpeg()
.toFile('output.jpg');특정 메타데이터 제거
javascript
// EXIF 제거하지만 다른 것은 보존
await sharp('input.jpg')
.resize(800, 600)
.withMetadata({ exif: false })
.jpeg()
.toFile('output.jpg');일괄 메타데이터 처리
일괄 읽기
javascript
const fs = require('fs').promises;
async function batchReadMetadata(directory) {
const files = await fs.readdir(directory);
const results = [];
for (const file of files) {
if (file.match(/\.(jpg|jpeg|png|webp)$/i)) {
try {
const metadata = await sharp(`${directory}/${file}`).metadata();
results.push({ file, metadata });
} catch (error) {
console.error(`${file} 메타데이터 읽기 실패:`, error.message);
}
}
}
return results;
}일괄 쓰기
javascript
async function batchWriteMetadata(directory, metadata) {
const files = await fs.readdir(directory);
for (const file of files) {
if (file.match(/\.(jpg|jpeg|png|webp)$/i)) {
try {
await sharp(`${directory}/${file}`)
.withMetadata(metadata)
.toFile(`${directory}/processed_${file}`);
} catch (error) {
console.error(`${file} 처리 실패:`, error.message);
}
}
}
}메타데이터 검증
메타데이터 무결성 검증
javascript
async function validateMetadata(filePath) {
try {
const metadata = await sharp(filePath).metadata();
// 기본 속성 확인
if (!metadata.width || !metadata.height) {
throw new Error('크기 정보 누락');
}
// 형식 확인
if (!metadata.format) {
throw new Error('형식 정보 누락');
}
// 채널 수 확인
if (!metadata.channels) {
throw new Error('채널 정보 누락');
}
return {
valid: true,
metadata
};
} catch (error) {
return {
valid: false,
error: error.message
};
}
}성능 최적화
필요한 메타데이터만 읽기
javascript
// 기본 정보만 읽기, EXIF 파싱 안 함
const metadata = await sharp('input.jpg')
.metadata({ pages: -1 });
// EXIF만 읽기
const metadata = await sharp('input.jpg')
.metadata({ exif: true });스트림 처리
javascript
const fs = require('fs');
// 스트림으로 메타데이터 읽기
const stream = fs.createReadStream('input.jpg');
const metadata = await sharp(stream).metadata();오류 처리
javascript
try {
const metadata = await sharp('input.jpg').metadata();
console.log('메타데이터:', metadata);
} catch (error) {
if (error.code === 'VipsForeignLoad') {
console.error('지원되지 않는 이미지 형식');
} else if (error.code === 'VipsForeignLoadLimit') {
console.error('이미지가 너무 큼');
} else {
console.error('메타데이터 읽기 실패:', error.message);
}
}