Skip to content

前端预览zip包信息

Source Code

ts
/**
 * zip 格式参考:https://blog.csdn.net/a200710716/article/details/51644421
 * @param {*} file 
 */

function loadZipAsync(file) {
  return new Promise(function(resolve, reject) {
    const reader = new FileReader()
    reader.addEventListener('load', function(event) {
      const buffer = event.target.result as Buffer
      const unit8Array = new Uint8Array(buffer)
        try {
          const comment = readEndOfCentral(unit8Array)
          resolve(comment)
        } catch (error) {
          reject(error)
        }
    })
    reader.addEventListener('error', (error) => {
      reject(error)
    })
    reader.readAsArrayBuffer(file)
  })
}

function readEndOfCentral(data) {
  const offset = lastIndexOfSignature(data, 'PK\x05\x06')
  if (offset === -1) {
    throw new Error('does not find the end of central directory')
  }

  let startIndex = offset + 20
  const commentLenSize = 2
  const zipCommentLength = readInt(data, startIndex, commentLenSize)
  if (!zipCommentLength) {
    return null
  }

  startIndex += commentLenSize

  const zipComment = data.slice(startIndex, startIndex + zipCommentLength)
  const zipCommentStr = decodeFile(zipComment)
  return zipCommentStr
}

function lastIndexOfSignature(data, sig) {
  const sig0 = sig.charCodeAt(0),
        sig1 = sig.charCodeAt(1),
        sig2 = sig.charCodeAt(2),
        sig3 = sig.charCodeAt(3)

  for (let i = data.length - 4; i >= 0; i--) {
    if (data[i] === sig0 && data[i + 1] === sig1 && data[i + 2] === sig2 && data[i + 3] === sig3) {
      return i
    }
  }

  return -1
}

/**
 * Get the next number with a given byte size.
 * @param {number} size the number of bytes to read.
 * @return {number} the corresponding number.
 */
function readInt(data, startIndex, size) {
  let result = 0,
      i
  for (i = startIndex + size - 1; i >= startIndex; i--) {
    result = (result << 8) + data[i]
  }
  return result
}

// 获取字符编码对应字符的字节长度(比如汉字是 2 个字节,ascii 是 1 个字节)
const _utf8len = (function() {
  const _utf8len = new Array(256)
  for (let i = 0; i < 256; i++) {
    _utf8len[i] = (i >= 252 ? 6 : i >= 248 ? 5 : i >= 240 ? 4 : i >= 224 ? 3 : i >= 192 ? 2 : 1)
  }
  _utf8len[254] = _utf8len[254] = 1 // Invalid sequence start
  return _utf8len
})()

function decodeFile(buf) {
  let str, i, out, c, c_len
  const len = buf.length

  // Reserve max possible length (2 words per char)
  // NB: by unknown reasons, Array is significantly faster for
  //     String.fromCharCode.apply than Uint16Array.
  let utf16buf = new Array(len * 2)

  for (out = 0, i = 0; i < len;) {
    c = buf[i++]
    // quick process ascii
    if (c < 0x80) { utf16buf[out++] = c; continue }

    c_len = _utf8len[c]
    // skip 5 & 6 byte codes
    if (c_len > 4) {
      utf16buf[out++] = 0xfffd
      i += c_len - 1
      continue
    }

    // apply mask on first byte
    c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07
    // join the rest
    while (c_len > 1 && i < len) {
      c = (c << 6) | (buf[i++] & 0x3f)
      c_len--
    }

    // terminated by end of string?
    if (c_len > 1) { utf16buf[out++] = 0xfffd; continue }

    if (c < 0x10000) {
      utf16buf[out++] = c
    } else {
      c -= 0x10000
      utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff)
      utf16buf[out++] = 0xdc00 | (c & 0x3ff)
    }
  }

  // shrinkBuf(utf16buf, out)
  if (utf16buf.length !== out) { // 含有 utf8 字符
    if (utf16buf.subarray) {
      utf16buf = utf16buf.subarray(0, out)
    } else {
      utf16buf.length = out
    }
  }

  return String.fromCharCode.apply(null, utf16buf)
}

export default loadZipAsync

Useage

ts
loadZipAsync(uploadFile.raw).then((res: string) => {
  console.log('包信息:', res)
}).catch((err) => {
  console.log('包解析错误!')
})