使用 exifreader 依赖读取图片 Exif 信息。可读取常规 jpeg、png、WebP 等格式。数码相机的 RAW、CR2 均可读取。
可在本地浏览器直接运行,不需要上传至服务器进行解析。
开发
安装依赖
css
pnpm i exifreader b64-to-blob
用于将 RAW、CR2 Exif 内 Thumbnail 字段转成 blob 后插入 img 标签内。
文件树
css
src/app/path/[[...path]]/card-display.tsx
src/app/path/context.tsx
src/components/action-dropdown.tsx
src/components/img-exif-modal/img-exif-context.tsx
src/components/img-exif-modal/modal.tsx
文件路径:src/components/img-exif-modal/img-exif-context.tsx
创建一个显示 exif 上下文文件。用于控制需要获取 exif 文件的地址。
typescript
'use client'
import createCtx from '@/lib/create-ctx'
import React from 'react'
import ImgExifModal from '@/components/img-exif-modal/modal'
export const ImgExifContext = createCtx<string>()
export const ImgExifProvider: React.FC<React.PropsWithChildren> = ({ children }) => {
return (
<ImgExifContext.ContextProvider value={''}>
{children}
<ImgExifModal />
</ImgExifContext.ContextProvider>
)
}
文件路径:src/components/img-exif-modal/modal.tsx
exif 信息弹窗文件
- 使用 ExifReader.load 方法加载远端图片地址。解析获得 exif 信息后,写入当前弹窗状态,并显示对应的信息。当没有信息时显示 No Exif data
- 当 exif 信息内有 Thumbnail 字段时,使用 b64-to-blob 将 base64 转换成 blob,createObjectURL 一个 img 可使用的地址。
typescript
'use client'
import React, { useState } from 'react'
import { Card, Descriptions, Drawer } from 'antd'
import { map, isEmpty, isUndefined, isNull } from 'lodash'
import { ImgExifContext } from '@/components/img-exif-modal/img-exif-context'
import ExifReader, { ThumbnailTags } from 'exifreader'
import { useMount } from 'ahooks'
import b64toBlob from 'b64-to-blob'
const filter_map = ['ApplicationNotes']
const Thumbnail: React.FC<{ item?: ThumbnailTags }> = ({ item }) => {
return (
item && (
<picture>
<img src={URL.createObjectURL(b64toBlob(item.base64, item.type))} alt="" />
</picture>
)
)
}
const ExifItem: React.FC = () => {
const img_path = ImgExifContext.useStore()
const [info, changeInfo] = useState<ExifReader.Tags | null>()
useMount(() => {
ExifReader.load(window.location.origin + img_path)
.then((data) => {
changeInfo(data)
})
.catch(() => {
changeInfo(null)
})
})
return (
<Card loading={isUndefined(info)}>
{isNull(info) ? (
'No Exif data'
) : (
<Descriptions column={1} bordered={true} labelStyle={{ width: '20em', textAlign: 'right' }}>
{map(info, (item, key) =>
filter_map.includes(key) ? null : (
<Descriptions.Item key={key} label={key}>
{key === 'Thumbnail' ? <Thumbnail item={info?.Thumbnail} /> : item.description}
</Descriptions.Item>
),
)}
</Descriptions>
)}
</Card>
)
}
const ImgExifModal: React.FC = () => {
const img_path = ImgExifContext.useStore()
const dispatch = ImgExifContext.useDispatch()
return (
<Drawer
title="图片信息"
placement="right"
open={!isEmpty(img_path)}
width={1000}
onClose={() => dispatch('')}
footer={false}
destroyOnClose={true}
>
<ExifItem />
</Drawer>
)
}
export default ImgExifModal
文件路径:src/components/action-dropdown.tsx
弹出下拉菜单,添加一个显示属性菜单项
javascript
'use client'
import React from 'react'
import { Dropdown } from 'antd'
import { ReaddirItemType } from '@/explorer-manager/src/type'
import { InfoOutlined } from '@ant-design/icons'
import { MenuProps } from 'antd/es/menu'
import { ImgExifContext } from '@/components/img-exif-modal/img-exif-context'
import { useReplacePathname } from '@/components/use-replace-pathname'
import { isGif, isImage, isRaw } from '@/components/preview/ext-rxp'
const ActionDropdown: React.FC<React.PropsWithChildren & { item: ReaddirItemType }> = ({ children, item }) => {
const name = item.name
const { staticPath } = useReplacePathname()
const changeImgExif = ImgExifContext.useDispatch()
const is_show_img_exif = isImage(name) || isRaw(name)
const preview_path = staticPath(name)
const menu: MenuProps = {
items: [],
}
if (is_show_img_exif) {
menu.items?.push({
icon: <InfoOutlined />,
label: '信息',
key: 'info',
onClick: () => {
changeImgExif(preview_path)
},
})
}
return (
<Dropdown menu={menu} trigger={['click']} destroyPopupOnHide={true}>
{children}
</Dropdown>
)
}
export default ActionDropdown
文件路径:src/app/path/[[...path]]/card-display.tsx
将下拉菜单加入卡片 item 的 extra 位置
javascript
...
import ActionDropdown from '@/components/action-dropdown'
import { EllipsisOutlined } from '@ant-design/icons'
...
<Card
title={item.name}
extra={
<ActionDropdown item={item}>
<Button icon={<EllipsisOutlined />} />
</ActionDropdown>
}
>
...
文件路径:src/app/path/context.tsx
将 ImgExifProvider 组件插入,完成显示图片 exif 信息功能
javascript
...
import { ImgExifProvider } from '@/components/img-exif-modal/img-exif-context'
export const PathContextProvider: React.FC<React.ProviderProps<ReaddirListType>> = ({ value, children }) => {
return (
<>
...
<ImgExifProvider>{children}</ImgExifProvider>
...
</>
)
}
效果
AI 生成的图片 parameters 信息也在内