最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

node文件資源管理器的解壓縮從零實(shí)現(xiàn)

 更新時(shí)間:2023年12月21日 15:19:03   作者:寒露  
這篇文章主要為大家介紹了node文件資源管理器的解壓縮從零實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

解壓縮

這里使用較為常用的 7z 來(lái)處理壓縮包,它可以解開常見的壓縮包格式

Unpacking only: APFS, AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS, UDF, UEFI, VDI, VHD, VHDX, VMDK, XAR and Z.

開發(fā)

預(yù)下載 mac 與 linux 版本的 7z 二進(jìn)制文件,放置于 explorer-manage/src/7zip/linux 與 /mac 目錄內(nèi)??汕巴?7z 官方進(jìn)行下載,下載鏈接。

也可以使用 7zip-bin 這個(gè)依賴,內(nèi)部包含所有環(huán)境可運(yùn)行的二進(jìn)制文件。由于項(xiàng)目是由鏡像進(jìn)行運(yùn)行,使用全環(huán)境的包會(huì)加大鏡像的體積。

所以這里單獨(dú)下載特定環(huán)境的下二進(jìn)制文件,可能版本會(huì)比較舊,最近更新為 2022/5/16 。目前最新的 2023/06/20@23.01 版本。

使用 node-7z 這個(gè)依賴處理 7z 的輸入輸出

安裝依賴

pnpm i node-7z

運(yùn)行文件

// https://laysent.com/til/2019-12-02_7zip-bin-in-alpine-docker
// https://www.npmjs.com/package/node-7z
// https://www.7-zip.org/download.html
// import sevenBin from '7zip-bin'
import node7z from 'node-7z'
import { parseFilePath } from './parse-path.mjs'
import path from 'path'
import { dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { formatPath } from '../../lib/format-path.mjs'
const __dirname = dirname(fileURLToPath(import.meta.url))
/**
 * @type {import('node-7z').SevenZipOptions}
 */
const base_option = {
  $bin: process.platform === 'darwin' ? path.join(__dirname, './mac/7zz') : path.join(__dirname, './linux/7zzs'),
  recursive: true,
  exclude: ['!__MACOSX/*', '!.DS_store'],
  latestTimeStamp: false,
}
/**
 * @param path {string}
 * @param out_path {string|undefined}
 * @param pwd {string | number | undefined}
 * @returns {import('node-7z').ZipStream}
 */
export const node7zaUnpackAction = (path, out_path = '', pwd = 'pwd') => {
  const join_path = formatPath(path)
  const { file_dir_path } = parseFilePath(join_path)
  return node7z.extractFull(join_path, formatPath(out_path) || `${file_dir_path}/`, {
    ...base_option,
    password: pwd,
  })
}
/**
 * @param path {string}
 * @param pwd {string | number | undefined}
 * @returns {import('node-7z').ZipStream}
 */
export const node7zListAction = (path, pwd = 'pwd') => {
  const join_path = formatPath(path)
  return node7z.list(join_path, { ...base_option, password: pwd })
}

簡(jiǎn)單封裝下 node7zaUnpackAction 與 node7zListAction 方法

  • node7zaUnpackAction:解壓縮方法
  • node7zListAction:查看當(dāng)前壓縮包內(nèi)容

explorer 客戶端展示

大致設(shè)計(jì)為彈窗模式,提供一個(gè)解壓縮位置,默認(rèn)當(dāng)前壓縮包位置。再提供一個(gè)密碼輸入欄,用于帶密碼的壓縮包解壓。

解壓縮一個(gè)超大包時(shí),可能會(huì)超過(guò) http 的請(qǐng)求超時(shí)時(shí)間,瀏覽器會(huì)主動(dòng)關(guān)閉這次請(qǐng)求。導(dǎo)致壓縮包沒(méi)有解壓縮完畢,請(qǐng)求就已經(jīng)關(guān)閉了。雖然 node 還在后臺(tái)進(jìn)行解壓縮。但是客戶端無(wú)法知道是否解壓縮完畢。

可通過(guò)延長(zhǎng) http 的請(qǐng)求超時(shí)時(shí)間。也可使用 stream 逐步輸出內(nèi)容的方式避免超時(shí),客戶端部分可以實(shí)時(shí)看到當(dāng)前解壓縮的進(jìn)度。類似像 AI 機(jī)器人提問(wèn)時(shí),文字逐字出現(xiàn)的效果。

查看壓縮包內(nèi)容

直接使用 server action 調(diào)用 node7zListAction 方法即可

解壓縮

使用 node-7z 的輸出流逐步輸出到瀏覽器

封裝一個(gè) post api 接口。

  • 監(jiān)聽 node-7z 返回的數(shù)據(jù)流 .on('data') 事件。
  • 對(duì)數(shù)據(jù)流做 encoder.encode(JSON.stringify(value) + ‘, ’) 格式化操作。方便客戶端讀取數(shù)據(jù)流。
  • 每秒往客戶端輸出一個(gè)時(shí)間戳避免請(qǐng)求超時(shí) stream.push({ loading: Date.now() })
  • 10 分鐘后關(guān)閉 2 的定時(shí)輸出,讓其自然超時(shí)。
  • 客戶端通過(guò) fetch 獲取數(shù)據(jù)流,具體可以看 unpack 方法

接口 api

import { NextRequest, NextResponse } from 'next/server'
import { node7zaUnpackAction } from '@/explorer-manager/src/7zip/7zip.mjs'
import { nodeStreamToIterator } from '@/explorer-manager/src/main.mjs'
const encoder = new TextEncoder()
const iteratorToStream = (iterator: AsyncGenerator) => {
  return new ReadableStream({
    async pull(controller) {
      const { value, done } = await iterator.next()
      if (done) {
        controller.close()
      } else {
        controller.enqueue(encoder.encode(JSON.stringify(value) + ', '))
      }
    },
  })
}
export const POST = async (req: NextRequest) => {
  const { path, out_path, pwd } = await req.json()
  try {
    const stream = node7zaUnpackAction(path, out_path, pwd)
    stream.on('data', (item) => {
      console.log('data', item.file)
    })
    const interval = setInterval(() => {
      console.log('interval', stream.info)
      stream.push({ loading: Date.now() })
    }, 1000)
    const timeout = setTimeout(
      () => {
        clearInterval(interval)
      },
      60 * 10 * 1000,
    )
    stream.on('end', () => {
      console.log('end', stream.info)
      stream.push({
        done: JSON.stringify(Object.fromEntries(stream.info), null, 2),
      })
      clearTimeout(timeout)
      clearInterval(interval)
      stream.push(null)
    })
    return new NextResponse(iteratorToStream(nodeStreamToIterator(stream)), {
      headers: {
        'Content-Type': 'application/octet-stream',
      },
    })
  } catch (e) {
    return NextResponse.json({ ret: -1, err_msg: e })
  }
}

客戶端彈窗組件

'use client'
import React, { useState } from 'react'
import { Card, Modal, Space, Table } from 'antd'
import UnpackForm from '@/components/unpack-modal/unpack-form'
import { isEmpty } from 'lodash'
import { useRequest } from 'ahooks'
import Bit from '@/components/bit'
import DateFormat from '@/components/date-format'
import { UnpackItemType } from '@/explorer-manager/src/7zip/types'
import { useUnpackPathDispatch, useUnpackPathStore } from '@/components/unpack-modal/unpack-path-context'
import { useUpdateReaddirList } from '@/app/path/readdir-context'
import { unpackListAction } from '@/components/unpack-modal/action'
let pack_list_path = ''
const UnpackModal: React.FC = () => {
  const unpack_path = useUnpackPathStore()
  const changeUnpackPath = useUnpackPathDispatch()
  const [unpack_list, changeUnpackList] = useState<UnpackItemType['list']>([])
  const { update } = useUpdateReaddirList()
  const packList = useRequest(
    async (form_val) => {
      pack_list_path = unpack_path
      const { pwd } = await form_val
      return unpackListAction(unpack_path, pwd)
    },
    {
      manual: true,
    },
  )
  const unpack = useRequest(
    async (form_val) => {
      pack_list_path = unpack_path
      unpack_list.length = 0
      const { out_path, pwd } = await form_val
      const res = await fetch('/path/api/unpack', {
        method: 'post',
        body: JSON.stringify({ path: unpack_path, out_path, pwd: pwd }),
      })
      if (res.body) {
        const reader = res.body.getReader()
        const decode = new TextDecoder()
        while (1) {
          const { done, value } = await reader.read()
          const decode_value = decode
            .decode(value)
            .split(', ')
            .filter((text) => Boolean(String(text).trim()))
            .map((value) => {
              try {
                return value ? JSON.parse(value) : { value }
              } catch (e) {
                return { value }
              }
            })
            .filter((item) => !item.loading)
            .reverse()
          !isEmpty(decode_value) && changeUnpackList((unpack_list) => decode_value.concat(unpack_list))
          if (done) {
            break
          }
        }
      }
      return Promise.resolve().then(update)
    },
    {
      manual: true,
    },
  )
  return (
    <Modal
      title="解壓縮"
      open={!isEmpty(unpack_path)}
      width={1000}
      onCancel={() => changeUnpackPath('')}
      footer={false}
      destroyOnClose={true}
    >
      <UnpackForm packList={packList} unpack={unpack} />
      <Space direction="vertical" style={{ width: '100%' }}>
        {pack_list_path === unpack_path && !isEmpty(unpack_list) && (
          <Card
            title="unpack"
            bodyStyle={{
              maxHeight: '300px',
              overflowY: 'scroll',
              paddingTop: 20,
              overscrollBehavior: 'contain',
            }}
          >
            {unpack_list.map(({ file, done }) => (
              <pre key={file || done}>{file || done}</pre>
            ))}
          </Card>
        )}
        {pack_list_path === unpack_path && !isEmpty(packList.data) && (
          <Card title="壓縮包內(nèi)容">
            {!isEmpty(packList.data?.data) && (
              <Table
                scroll={{ x: true }}
                rowKey={({ file }) => file}
                columns={[
                  { key: 'file', dataIndex: 'file', title: 'file' },
                  {
                    key: 'size',
                    dataIndex: 'size',
                    title: 'size',
                    width: 100,
                    render: (size) => {
                      return <Bit>{size}</Bit>
                    },
                  },
                  {
                    key: 'sizeCompressed',
                    dataIndex: 'sizeCompressed',
                    title: 'sizeCompressed',
                    width: 150,
                    render: (size) => {
                      return <Bit>{size}</Bit>
                    },
                  },
                  {
                    key: 'datetime',
                    dataIndex: 'datetime',
                    title: 'datetime',
                    width: 180,
                    render: (date) => <DateFormat>{new Date(date).getTime()}</DateFormat>,
                  },
                ]}
                dataSource={packList.data?.data}
              />
            )}
            {packList.data?.message && <p>{packList.data?.message}</p>}
          </Card>
        )}
      </Space>
    </Modal>
  )
}
export default UnpackModal

測(cè)試用逐字輸出

每秒往客戶端輸出當(dāng)前時(shí)間。持續(xù) 10 分鐘。

import { iteratorToStream, nodeStreamToIterator } from '@/explorer-manager/src/main.mjs'

function sleep(time: number) {
  return new Promise((resolve) =&gt; {
    setTimeout(resolve, time)
  })
}

const encoder = new TextEncoder()

async function* makeIterator() {
  let length = 0
  while (length &gt; 60 * 10) {
    await sleep(1000)
    yield encoder.encode(`&lt;p&gt;${length} ${new Date().toLocaleString()}&lt;/p&gt;`)

    length += 1
  }
}

export async function POST() {
  return new Response(iteratorToStream(nodeStreamToIterator(makeIterator())), {
    headers: { 'Content-Type': 'application/octet-stream' },
  })
}

export async function GET() {
  return new Response(iteratorToStream(nodeStreamToIterator(makeIterator())), {
    headers: { 'Content-Type': 'html' },
  })
}

效果

git-repo

yangWs29/share-explorer

以上就是node文件資源管理器的解壓縮從零實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于node文件資源解壓縮的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • windows實(shí)現(xiàn)npm和cnpm安裝步驟

    windows實(shí)現(xiàn)npm和cnpm安裝步驟

    這篇文章主要介紹了windows實(shí)現(xiàn)npm和cnpm安裝步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • Node.js中用D3.js的方法示例

    Node.js中用D3.js的方法示例

    這篇文章主要給大家介紹了在Node.js中用D3.js的方法,文中分別介紹了如何安裝模塊和一小段簡(jiǎn)單的示例代碼,有需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-01-01
  • Nodejs之http的表單提交

    Nodejs之http的表單提交

    這篇文章主要為大家詳細(xì)介紹了Nodejs之http的表單提交,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • 如何初始化一個(gè)nodejs工程(附完整步驟)

    如何初始化一個(gè)nodejs工程(附完整步驟)

    nodejs初始化的文章相當(dāng)?shù)亩?多看幾篇文章基本就可以大體了解了,這篇文章主要介紹了如何初始化一個(gè)nodejs工程的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-08-08
  • nodejs中使用monk訪問(wèn)mongodb

    nodejs中使用monk訪問(wèn)mongodb

    最近在做nodejs的web開發(fā),初次接觸到mongoDB這個(gè)數(shù)據(jù)庫(kù)。分享點(diǎn)使用經(jīng)驗(yàn)
    2014-07-07
  • 基于socket.io和node.js搭建即時(shí)通信系統(tǒng)

    基于socket.io和node.js搭建即時(shí)通信系統(tǒng)

    socket.IO是一個(gè)websocket庫(kù),包括了客戶端的js和服務(wù)器端的nodejs。官方地址:http://socket.io
    2014-07-07
  • express如何使用session與cookie的方法

    express如何使用session與cookie的方法

    本篇文章主要介紹了express如何使用session與cookie的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • Node.js自動(dòng)生成API文檔的實(shí)現(xiàn)

    Node.js自動(dòng)生成API文檔的實(shí)現(xiàn)

    本文主要介紹了Node.js自動(dòng)生成API文檔,包含基于swagger-jsdoc+swagger-ui-express快速實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • Node.js版本升級(jí)如何修改模塊默認(rèn)的保存位置

    Node.js版本升級(jí)如何修改模塊默認(rèn)的保存位置

    這篇文章主要給大家介紹了關(guān)于Node.js版本升級(jí)如何修改模塊默認(rèn)的保存位置,文中通過(guò)代碼以及圖文介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用node.js具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2024-05-05
  • export?default?和?export?的使用方式示例詳解

    export?default?和?export?的使用方式示例詳解

    這篇文章主要介紹了export?default?和?export?的使用方式,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08

最新評(píng)論

德惠市| 兴国县| 龙州县| 丰都县| 铜陵市| 玛沁县| 灌云县| 焉耆| 鹤壁市| 泸西县| 长宁县| 洛川县| 喜德县| 额尔古纳市| 泸西县| 铁岭市| 宝丰县| 阿合奇县| 东阿县| 旬邑县| 马公市| 桃园县| 乌鲁木齐县| 曲周县| 耒阳市| 阿图什市| 兰坪| 乐都县| 横山县| 金塔县| 福泉市| 天镇县| 阜康市| 穆棱市| 屏边| 遂平县| 宝清县| 泾源县| 鱼台县| 长兴县| 从化市|