从零开始-文件资源管理器-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

相关推荐
天宇&嘘月2 小时前
web第三次作业
前端·javascript·css
小王不会写code2 小时前
axios
前端·javascript·axios
发呆的薇薇°3 小时前
vue3 配置@根路径
前端·vue.js
luckyext4 小时前
HBuilderX中,VUE生成随机数字,vue调用随机数函数
前端·javascript·vue.js·微信小程序·小程序
小小码农(找工作版)4 小时前
JavaScript 前端面试 4(作用域链、this)
前端·javascript·面试
前端没钱4 小时前
前端需要学习 Docker 吗?
前端·学习·docker
前端郭德纲4 小时前
前端自动化部署的极简方案
运维·前端·自动化
海绵宝宝_4 小时前
【HarmonyOS NEXT】获取正式应用签名证书的签名信息
android·前端·华为·harmonyos·鸿蒙·鸿蒙应用开发
码农土豆5 小时前
chrome V3插件开发,调用 chrome.action.setIcon,提示路径找不到
前端·chrome
鱼樱前端5 小时前
深入JavaScript引擎与模块加载机制:从V8原理到模块化实战
前端·javascript