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

相关推荐
左夕9 小时前
分不清apply,bind,call?看这篇文章就够了
前端·javascript
Zha0Zhun10 小时前
一个使用ViewBinding封装的Dialog
前端
兆子龙10 小时前
从微信小程序 data-id 到 React 列表性能优化:少用闭包,多用 data-*
前端
滕青山10 小时前
文本行过滤/筛选 在线工具核心JS实现
前端·javascript·vue.js
时光不负努力10 小时前
编程常用模式集合
前端·javascript·typescript
恋猫de小郭10 小时前
Apple 的 ANE 被挖掘,AI 硬件公开,宣传的 38 TOPS 居然是"数字游戏"?
前端·人工智能·ios
小岛前端10 小时前
Node.js 宣布重大调整,运行十年的规则要改了!
前端·node.js
OpenTiny社区10 小时前
OpenTiny NEXT-SDK 重磅发布:四步把你的前端应用变成智能应用
前端·javascript·ai编程
梦想CAD控件10 小时前
在线CAD开发包结构与功能说明
前端·javascript·vue.js
张拭心10 小时前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能