从零开始-文件资源管理器-10-获取图片 exif 信息

使用 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 信息弹窗文件

  1. 使用 ExifReader.load 方法加载远端图片地址。解析获得 exif 信息后,写入当前弹窗状态,并显示对应的信息。当没有信息时显示 No Exif data
  2. 当 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 信息也在内

git-repo

yangWs29/share-explorer

相关推荐
一张假钞1 分钟前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行1 分钟前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_593758103 分钟前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox
掘金一周6 分钟前
金石焕新程 >> 瓜分万元现金大奖征文活动即将回归 | 掘金一周 4.3
前端·人工智能·后端
三翼鸟数字化技术团队24 分钟前
Vue自定义指令最佳实践教程
前端·vue.js
Jasmin Tin Wei1 小时前
蓝桥杯 web 学海无涯(axios、ecahrts)版本二
前端·蓝桥杯
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
转转技术团队1 小时前
代码变更暗藏危机?代码影响范围分析为你保驾护航
前端·javascript·node.js
Mintopia1 小时前
Node.js高级实战:自定义流与Pipeline的高效数据处理 ——从字母生成器到文件管道的深度解析
前端·javascript·node.js
Mintopia1 小时前
Three.js深度解析:InstancedBufferGeometry实现动态星空特效 ——高效渲染十万粒子的底层奥秘
前端·javascript·three.js