node文件资源管理器的图片预览从零实现

 更新时间:2023年12月21日 15:38:25   作者:寒露  
这篇文章主要为大家介绍了node文件资源管理器的图片预览从零实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

 使用技术

  • 使用 Next.js 的 Image 组件显示图片。自带图片压缩生成快速预览的 webp 格式图片
  • 使用 antd 的 PreviewGroup 组件实现原图浏览,自带缩小、放大、上一张、下一张等功能

功能实现

文件树

explorer/src/app/path/[[...path]]/card-display.tsx
explorer/src/app/path/[[...path]]/page.tsx
explorer/src/components/preview/ext-rxp.tsx
explorer/src/components/preview/index.tsx
explorer/src/components/preview/proview-group-context.tsx
explorer/src/components/use-replace-pathname.ts

文件路径:explorer/src/components/preview/ext-rxp.tsx

一些判断文件后缀名方法

export const ImgRxp = /\.(jpg|jpeg|gif|png|webp|ico)$/i
const RawRxp = /\.(cr2|arw)/i
const GifRxp = /\.(gif)$/i
const ZipRxp = /\.(zip|rar|7z|tar\.xz|tar)(\.+\d+)?$/i
const VideoRxp = /\.(mp4|mkv|mov|wmv|avi|avchd|flv|f4v|swf)(\.+\d+)?$/i
export const isRaw = (path: string) => RawRxp.test(path)
export const isImage = (path: string) => ImgRxp.test(path)
export const isGif = (path: string) => GifRxp.test(path)
export const isZip = (path: string) => ZipRxp.test(path)
export const isVideo = (path: string) => VideoRxp.test(path)

文件路径:explorer/src/components/preview/index.tsx

预览封装组件,根据是否为文件夹、视频、图片、压缩包显示不同的 icon。

点击图片时,使用 antd 的 PreviewGroup 组件查看原图。

import React from 'react'
import { FileOutlined, FileZipOutlined, FolderOutlined, VideoCameraOutlined } from '@ant-design/icons'
import Image from 'next/image'
import { isGif, isImage, isVideo, isZip } from '@/components/preview/ext-rxp'
import { usePreviewGroupDispatch } from '@/components/preview/proview-group-context'
import { ReaddirItemType } from '@/explorer-manager/src/type'
import { useReplacePathname } from '@/components/use-replace-pathname'
const Preview: React.FC<{ item: ReaddirItemType }> = ({ item }) => {
  const previewGroupDispatch = usePreviewGroupDispatch()
  const { name, is_directory } = item
  const { staticPath, joinSearchPath } = useReplacePathname()
  if (is_directory) {
    return <FolderOutlined />
  }
  if (isVideo(name)) {
    return <VideoCameraOutlined />
  }
  if (isImage(name)) {
    const image_path = staticPath(name)
    return (
      <Image
        onClick={() => previewGroupDispatch(name)}
        src={image_path}
        alt={name}
        fill
        sizes="375px"
        style={{
          objectFit: 'scale-down', //"contain" | "cover" | "fill" | "none" | "scale-down"
        }}
        unoptimized={isGif(image_path)}
        placeholder="empty"
      />
    )
  }
  if (isZip(name)) {
    return <FileZipOutlined />
  }
  return <FileOutlined />
}
export default Preview

文件路径:explorer/src/components/preview/proview-group-context.tsx

antd PreviewGroup 组件封装。

需要在顶层目录插入 PreviewGroupProvider 上下文组件,导出 usePreviewGroup、usePreviewGroupDispatch 读写方法。

'use client'
import React from 'react'
import { Image as AntdImage } from 'antd'
import { findIndex } from 'lodash'
import { isImage } from '@/components/preview/ext-rxp'
import { useReplacePathname } from '@/components/use-replace-pathname'
import createCtx from '@/lib/create-ctx'
import { useReaddirContext } from '@/app/path/readdir-context'
export const PreviewGroupContent = createCtx<string>()
export const usePreviewGroup = PreviewGroupContent.useStore
export const usePreviewGroupDispatch = PreviewGroupContent.useDispatch
const AntdImagePreviewGroup: React.FC<React.PropsWithChildren> = ({ children }) => {
  const { staticPath } = useReplacePathname()
  const readdir_list = useReaddirContext()
  const image_list = readdir_list.filter((item) => isImage(item.name))
  const name = usePreviewGroup()
  const previewGroupDispatch = usePreviewGroupDispatch()
  return (
    <AntdImage.PreviewGroup
      preview={{
        visible: !!name,
        current: findIndex(image_list, { name }),
        onVisibleChange: () => {
          previewGroupDispatch('')
        },
        onChange: (current) => {
          previewGroupDispatch(image_list[current].name)
        },
      }}
      items={image_list.map(({ name }) => staticPath(name))}
    >
      {children}
    </AntdImage.PreviewGroup>
  )
}
const PreviewGroupProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  return (
    <PreviewGroupContent.ContextProvider value={''}>
      <AntdImagePreviewGroup>{children}</AntdImagePreviewGroup>
    </PreviewGroupContent.ContextProvider>
  )
}
export default PreviewGroupProvider

文件路径:explorer/src/components/use-replace-pathname.ts

添加一个获取不同路径的hooks

  • replace\_pathname 将不需要的一级路径 /path/ 过滤掉
  • joinSearchPath 拼接过滤掉 /path/ 的 pathname
  • joinPath 拼接未过滤的 pathname
  • staticPath 拼接得到获取文件地址
import { usePathname } from 'next/navigation'
export const pathExp = /(^\/)?path/
export const encodePathItem = (path: string) => {
  return path
    .split('/')
    .map((text) => encodeURIComponent(text))
    .join('/')
}
export const useReplacePathname = () => {
  const pathname = decodeURIComponent(usePathname() || '')
  const replace_pathname = pathname.replace(pathExp, '')
  const joinSearchPath = (path: string) => encodePathItem(`${replace_pathname}/${path}`)
  const joinPath = (path: string) => encodePathItem(`${pathname}/${path}`)
  const staticPath = (path: string) => `/static${joinSearchPath(path)}`
  return {
    pathname: pathname,
    replace_pathname: replace_pathname,
    joinSearchPath: joinSearchPath,
    joinPath: joinPath,
    staticPath: staticPath,
  }
}

文件路径:explorer/src/app/path/[[...path]]/card-display.tsx

将 Preview 组件插入 List Item 内

...
import Preview from '@/components/preview'
const CardDisplay: React.FC = () => {
  const pathname = usePathname()
  const readdir = useReaddirContext()
  const column = useCardColumnContext()
  return (
    <List
...
                  <div style={{ position: 'absolute', width: '100%', height: '100%' }}>
                    <Preview item={item} />
                  </div>
...

文件路径:explorer/src/app/path/[[...path]]/page.tsx

将 PreviewGroupProvider 组件插入最顶部

...
import PreviewGroupProvider from '@/components/preview/proview-group-context'
const Page: React.FC = () => {
  const display_type = useDisplayTypeContext()
  return <PreviewGroupProvider>{display_type === 'table' ? <TableDisplay /> : <CardDisplay />}</PreviewGroupProvider>
}
export default Page

效果

git-repo

yangWs29/share-explorer

以上就是node文件资源管理器的图片预览从零实现的详细内容,更多关于node文件图片预览的资料请关注脚本之家其它相关文章!

相关文章

  • node作为中间服务层如何发送请求(发送请求的实现方法详解)

    node作为中间服务层如何发送请求(发送请求的实现方法详解)

    node作为中间服务层如何发送请求?下面小编就为大家分享一下发送请求的实现方法,具有很好的参考价值,希望对大家有所帮助
    2018-01-01
  • node事件循环中事件执行的顺序

    node事件循环中事件执行的顺序

    在浏览器环境下我们的js有一套自己的事件循环,同样在node环境下也有一套类似的事件循环。本文就详细的来介绍一下,感兴趣的可以了解一下
    2021-08-08
  • 基于node实现websocket协议

    基于node实现websocket协议

    这篇文章主要介绍了基于node实现websocket协议的相关资料,需要的朋友可以参考下
    2016-04-04
  • Windows下安装Bun像Node或Deno的现代JS运行时

    Windows下安装Bun像Node或Deno的现代JS运行时

    这篇文章主要为大家介绍了一款像Node或Deno的现代JavaScript运行时的bun在Windows下安装过程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • 究竟什么是Node.js?Node.js有什么好处?

    究竟什么是Node.js?Node.js有什么好处?

    这篇文章主要介绍了究竟什么是Node.js?Node.js有什么好处?,为试图解释什么是 Node.js,本文将简要介绍一些背景信息:它要解决的问题,它如何工作,如何运行一个简单应用程序,最后,Node 在什么情况下是一个好的解决方案,需要的朋友可以参考下
    2015-05-05
  • 三分钟教会你用nodejs操作mysql数据库

    三分钟教会你用nodejs操作mysql数据库

    MySQL数据库作为最流行的开源数据库,基本上是每个web开发者必须要掌握的数据库程序之一了,基本使用 node.js上,最受欢迎的mysql包就是mysql模块,下面这篇文章主要给大家介绍了关于如何通过三分钟教会你用nodejs操作mysql数据库,需要的朋友可以参考下
    2023-05-05
  • websocket+node.js实现实时聊天系统问题咨询

    websocket+node.js实现实时聊天系统问题咨询

    最近新学习websocket,做了一个实时聊天。用Node.js搭建的服务:serevr.js. 两个相互通信页面:client.html 和server.html 但是就是有很多问题,下面通过本文给大家分享下
    2017-05-05
  • Visual Studio Code中npm脚本找不到图文解决办法

    Visual Studio Code中npm脚本找不到图文解决办法

    这篇文章主要给大家介绍了关于Visual Studio Code中npm脚本找不到的图文解决办法,做前端开发如果项目达到了一定的规模就离不开npm了,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • Node.js websocket使用socket.io库实现实时聊天室

    Node.js websocket使用socket.io库实现实时聊天室

    这篇文章主要为大家详细介绍了Node.js websocket使用socket.io库实现实时聊天室,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-02-02
  • node.js监听文件变化的实现方法

    node.js监听文件变化的实现方法

    这篇文章主要给大家介绍了关于node.js监听文件变化的实现方法,文中通过示例代码介绍的非常详细,对大家学习或者使用node.js具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-04-04

最新评论