元數據 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);
}
}