feat(download-code): support download zip for not support browsers (#703)

* feat(download-code): support download zip for not support browsers

* feat(download-code): support download zip for not support browsers - review

* feat(download-code): support download zip for not support browsers - review
This commit is contained in:
wenmine 2024-08-06 14:54:32 +08:00 committed by GitHub
parent 0d36f2d344
commit 3a204624af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 102 additions and 15 deletions

View File

@ -89,7 +89,6 @@
"eslint-linter-browserify": "8.57.0",
"file-saver": "^2.0.5",
"html2canvas": "^1.4.1",
"jszip": "^3.10.1",
"monaco-editor": "0.33.0",
"prettier": "2.7.1",
"sortablejs": "^1.14.0",

View File

@ -30,7 +30,9 @@
"vite": "^4.3.7",
"vitest": "^1.4.0"
},
"dependencies": {},
"dependencies": {
"jszip": "^3.10.1"
},
"peerDependencies": {
"@opentiny/vue-renderless": "^3.14.0",
"vue": "^3.4.15"

View File

@ -0,0 +1,67 @@
/**
* Copyright (c) 2023 - present TinyEngine Authors.
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
// browser File System Access API encapsulation
import JSZIP from 'jszip'
/**
* 下载文件到本地
* @param {Blob} blobData 文件二进制数据
* @param {string} fileName 文件名
*/
export function saveAs(blobData, fileName) {
const zipLink = document.createElement('a')
zipLink.download = fileName
zipLink.style.display = 'none'
zipLink.href = URL.createObjectURL(blobData)
document.body.appendChild(zipLink)
zipLink.click()
document.body.removeChild(zipLink)
}
/**
* 创建一个zip
*/
export const createZip = () => {
return new JSZIP()
}
/**
* 往zip里面写入文件夹和文件
* @param {Array<FileInfo>} filesInfo 文件信息
* FileInfo.filePath 文件相对路径'/'相连
* FileInfo.fileContent 文件内容
* @param {ZipExtraInfo} ZipExtraInfo zip额外信息
* {string} zipName 打出来的zip名称
* {JSZIP} zipHandle 创建好的zip句柄可以不传不传就用新的
*/
export const writeZip = (filesInfo, { zipHandle, zipName } = {}) => {
let zip = zipHandle
if (!zipHandle) {
zip = createZip()
}
filesInfo.forEach(({ filePath, fileContent }) => {
const file = filePath.split('/')
const fileName = file.pop()
const path = file.join('/')
if (path) {
zip.folder(path).file(fileName, fileContent)
} else {
zip.file(fileName, fileContent)
}
})
// 把打包的内容异步转成blob二进制格式
return zip.generateAsync({ type: 'blob' }).then((content) => {
// content就是blob数据
saveAs(content, `${zipName}.zip`)
})
}

View File

@ -1,17 +1,23 @@
/**
* Copyright (c) 2023 - present TinyEngine Authors.
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
* Copyright (c) 2023 - present TinyEngine Authors.
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
// browser File System Access API encapsulation
import { createZip, writeZip } from './fszip'
// 支持file system api的条件存在这个方法 && 不处于iframe中
export const isSupportFileSystemAccess =
Object.prototype.hasOwnProperty.call(window, 'showDirectoryPicker') && window.self === window.top
/**
* 获取用户选择并授权的文件夹根路径
* @param {*} options
@ -20,7 +26,7 @@
*/
export const getUserBaseDirHandle = async (options = {}) => {
if (!window.showOpenFilePicker) {
throw new Error('不支持的浏览器!')
return createZip()
}
const dirHandle = await window.showDirectoryPicker({ mode: 'readwrite', ...options })
return dirHandle
@ -166,16 +172,29 @@ export const writeFile = async (handle, { filePath, fileContent }) => {
* @param {Array<FileInfo>} filesInfo 文件信息
* FileInfo.filePath 文件相对路径
* FileInfo.fileContent 文件内容
* @param {Boolean} supportZipCache 是否支持zip缓存zip缓存可能会导致文件不能及时更新默认不缓存
*
*/
export const writeFiles = async (baseDirHandle, filesInfo) => {
export const writeFiles = async (
baseDirHandle,
filesInfo,
zipName = 'tiny-engine-generate-code',
supportZipCache = false
) => {
if (!filesInfo?.length) {
return
}
if (!isSupportFileSystemAccess) {
const zipInfo = { zipName, zipHandle: supportZipCache && baseDirHandle }
await writeZip(filesInfo, zipInfo)
return
}
let directoryHandle = baseDirHandle
if (!directoryHandle) {
directoryHandle = await window.showDirectoryPicker({ mode: 'readwrite' })
}
await Promise.all(filesInfo.map((fileInfo) => writeFile(baseDirHandle, fileInfo)))
await Promise.all(filesInfo.map((fileInfo) => writeFile(directoryHandle, fileInfo)))
}