123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- import { GLOBAL } from '@/static/Global.js';
- import JsFn from '@/utils/js-fn.js';
- console.log(GLOBAL)
- const { BlobServiceClient, BlobClient } = require("@azure/storage-blob")
- //const s = "?sv=2019-12-12&ss=b&srt=sco&st=2020-12-22T10%3A09%3A08Z&se=2020-12-23T10%3A09%3A08Z&sp=rwdxftlacup&sig=EHyNiA5uoBPHQAHfISLei%2BWdWnldHzOYYR7ZYdtFtRo%3D"
- //获取文件后缀和类型
- function getExAndType(fileName) {
- let ex = fileName.substring(fileName.lastIndexOf('.') + 1)
- let type = 'other'
- ex = ex.toUpperCase()
- for (let key in GLOBAL.CONTENT_TYPES) {
- if (GLOBAL.CONTENT_TYPES[key].indexOf(ex) != -1) {
- type = key
- break
- }
- }
- return {
- ex, type
- }
- }
- export default class BlobTool {
- /**
- * 初始化Blob,需要先调用授权API
- * @param {string} blobUrl blob地址
- * @param {string} container 容器名称
- * @param {string} sasString 授权
- * */
- constructor(blobUrl, container, sasString, scope) {
- this.initBlob(blobUrl, container, sasString, scope)
- }
- /**
- * 初始化Blob,需要先调用授权API
- * @param {string} blobUrl blob地址
- * @param {string} container 容器名称
- * @param {string} sasString 授权
- * @param {string} scope 学校(school)/个人(private) 计算空间大小
- * */
- initBlob(blobUrl, container, sasString, scope) {
- if (blobUrl && container && sasString, scope) {
- //初始化containerClient
- this.blobService = new BlobServiceClient(blobUrl + sasString)
- let containerClient = this.blobService.getContainerClient(container)
- if (containerClient) {
- this.containerClient = containerClient
- this.container = container
- this.blobUrl = blobUrl
- this.sasString = sasString
- if (scope == 'private') {
- this.blobSpace = GLOBAL.PRIVATE_SPACE
- } else if (scope == 'school') {
- this.blobSpace = GLOBAL.SCHOOL_SPACE
- }
- } else {
- throw new Error("参数错误,初始化失败")
- }
- } else {
- throw new Error("初始化参数不完整")
- }
- }
- /**
- * 获取容器信息 (授权失败,需要账号级别的授权)
- * @param {object}
- */
- getProperties(options) {
- return new Promise((r, j) => {
- //const blockBlobClient = this.containerClient.getBlockBlobClient('res/基础操作范例_16x9.HTE') //blob获取成功
- this.containerClient.getProperties(options).then(
- res => {
- console.log('获取信息成功')
- console.log(res)
- r(res)
- },
- err => {
- console.log('获取信息失败')
- console.log(err)
- j(err)
- }
- )
- })
- }
- /**
- * 上传文件方法,带回调上传进度
- * @param {any} file 文件对象
- * @param {any} path 文件夹路径
- * @param {any} option 官方可配置项
- * @param {any} checkSize 上传时是否检查容器空间 官方可配置项。默认需要检查大小
- * @returns {object} {url, name,size,createTime,extension,type}
- */
- upload(file, path, option, checkSize = true) {
- return new Promise(async (r, j) => {
- //检查容器空间大小
- if (checkSize) {
- if (!this.totalSize) {
- try {
- this.totalSize = await this.getContainerSize()
- } catch (e) {
- this.totalSize = -1
- j({
- spaceError: '容器空间计算失败,无法上传文件'
- })
- }
- }
- if (this.totalSize == -1) {
- j({
- spaceError: 'Blob空间计算失败,无法上传文件'
- })
- } else if (this.totalSize > this.blobSpace) {
- j({
- spaceError: 'Blob空间已满,无法上传'
- })
- }
- }
- const blockBlobClient = this.containerClient.getBlockBlobClient(path + "/" + file.name)
- blockBlobClient.uploadBrowserData(file, option).then(
- res => {
- let url = decodeURIComponent(res._response.request.url)
- url = url.substring(0, url.lastIndexOf('?'))
- let info = getExAndType(file.name)
- if (this.totalSize) {
- this.totalSize += res._response.request.body.size
- }
- r({
- url: url,
- blob: '/' + path + "/" + file.name,
- name: file.name,
- size: res._response.request.body.size,
- createTime: res.lastModified.getTime(),
- extension: info.ex,
- type: info.type
- })
- },
- err => {
- j(err)
- }
- )
- })
- }
- /**
- * 处理HTEX文件类型
- * @param {blobList} fileList
- */
- handleHTEXFile(fileList) {
- let parseRes = []
- let names = []
- fileList.forEach((item, index) => {
- if (item.url.indexOf('/res/') > 0) {
- let fileItem = {}
- let startIndex = JsFn.findChartIndex(item.blob, '/', 1)
- let endIndex = JsFn.findChartIndex(item.blob, '/', 2)
- let name = item.blob.substring(startIndex + 1, endIndex)
- let nameIndex = names.indexOf(name)
- if (nameIndex == -1) {
- fileItem.url = this.blobUrl+ '/' + this.container +'/res/' + name
- fileItem.blob = `/res/${name}/index.json`
- fileItem.name = name + '.HTEX'
- fileItem.size = item.size
- fileItem.createTime = item.createTime
- fileItem.extension = 'HTEX'
- fileItem.type = 'res'
- names.push(name)
- parseRes.push(fileItem)
- } else {
- parseRes[nameIndex].size += item.size
- }
- } else {
- parseRes.push(item)
- }
- })
- return parseRes
- }
- /**
- * 列出所有(查询)
- * @param {Object} option ContainerListBlobsOptions
- * eg: option.prefix = 'res' 只查res文件夹下的blob
- * @returns {object} {blobList, continuationToken}
- */
- async listBlob(option, hendleHTEX = true) {
- return new Promise(async (r, j) => {
- let blobList = []
- if (this.containerClient) {
- let iter = this.containerClient.listBlobsFlat(option ? option : {});
- let blobItem = await iter.next();
- while (!blobItem.done) {
- let blobName = blobItem.value.name
- let info = getExAndType(blobItem.value.name)
- blobList.push(
- {
- url: this.blobUrl + '/' + this.container + '/' + blobName,
- blob: '/' + blobName,
- // name: blobName.substring(JsFn.findChartIndex(blobName, '/', 0) + 1),
- name: blobName.substring(blobName.lastIndexOf('/') + 1),
- size: blobItem.value.properties.contentLength,
- createTime: blobItem.value.properties.createdOn.getTime(),
- extension: info.ex,
- type: info.type
- }
- )
- blobItem = await iter.next();
- }
- if (hendleHTEX) {
- blobList = this.handleHTEXFile(blobList)
- }
- console.log('----', blobList)
- r({
- blobList,
- continuationToken: 'end'
- })
- } else {
- j('containerClient 错误')
- }
- })
- }
- /**
- * 分页列出(查询)
- * @param {Object} option ContainerListBlobsOptions
- * eg: option.prefix = 'res' 只查res文件夹下的blob
- * @param {Object} pageInfo
- * eg: pageInfo.maxPageSize 当前请求条数
- * eg: pageInfo.continuationToken 首次请求不需要,后面需要(首次请求会返回,下次需要传入)
- * @returns {object} {blobList, continuationToken}
- */
- async listBlobByPage(option, pageInfo) {
- return new Promise(async (r, j) => {
- let page = {}
- let blobList = []
- if (pageInfo && JSON.stringify(pageInfo) != '{}') {
- page.maxPageSize = pageInfo.maxPageSize ? pageInfo.maxPageSize : 50
- if (pageInfo.continuationToken) {
- page.continuationToken = pageInfo.continuationToken ? pageInfo.continuationToken : ''
- }
- }
- if (this.containerClient) {
- let iterator = this.containerClient.listBlobsFlat(option ? option : {}).byPage(page)
- let response = (await iterator.next()).value
- let prefixLen = response.prefix ? response.prefix.length + 1 : 0
- for (const blob of response.segment.blobItems) {
- let info = getExAndType(blob.name)
- blobList.push(
- {
- url: response.serviceEndpoint + response.containerName + '/' + blob.name,
- blob: '/' + blob.name,
- name: blob.name.substring(prefixLen),
- size: blob.properties.contentLength,
- createTime: blob.properties.lastModified.getTime(),
- extension: info.ex,
- type: info.type
- }
- )
- }
- let continuationToken = response.continuationToken ? response.continuationToken : 'end'
- r({
- blobList,
- continuationToken
- })
- } else {
- j('containerClient 错误')
- }
- })
- }
- /**
- * 删除Blob
- * @param {string} filePath 文件url + 容器 之后的路径
- */
- deleteBlob(filePath) {
- if (filePath) {
- filePath = filePath.substring(1)
- }
- return new Promise((r, j) => {
- this.containerClient.deleteBlob(filePath).then(
- res => {
- r(res)
- },
- err => {
- j(err)
- }
- )
- })
- }
- /**
- * 批量删除Blob 官方API授权失败(需要账号级别的授权,批量删除请访问后端API)
- * @param {string} files 文件url + 容器 之后的路径
- */
- deleteBlobs(files) {
- this.getProperties()
- let blobBatchClient = this.blobService.getBlobBatchClient()
- return new Promise((r, j) => {
- for (let i in files) {
- files[i] = files[i].substring(0, files[i].lastIndexOf('?'))
- }
- blobBatchClient.deleteBlobs(files, this.blobService.credential).then(
- res => {
- console.log('批量删除成功')
- },
- err => {
- console.log('批量删除失败')
- }
- )
- })
- }
- /**
- * 批量删除Blob 循环操作
- * @param {string} files
- */
- deleteBlobBatch(files) {
- return new Promise((r, j) => {
- let promises = []
- for (let item of files) {
- let f = item.substring(1)
- promises.push(this.containerClient.deleteBlob(f))
- }
- Promise.all(promises).then(
- res => {
- r(res)
- },
- err => {
- j(err)
- }
- )
- })
- }
- /**
- * 复制单个Blob
- * @param {string} targetUrl
- * @param {string} sourceUrl
- * 1、目标url(sourceUrl)不能出现中文或UTF-8码的字符, 如果有需要进行URI编码,否则会报错;
- * 2、目标url(sourceUrl)如果需要授权的容器,则url需要凭借授权。
- */
- copyBlob(targetUrl, sourceUrl, sas) {
- return new Promise((r, j) => {
- let newBlob = this.containerClient.getBlobClient(targetUrl)
- let encodeUrl = encodeURI(sourceUrl)
- // sas = "?sv=2020-02-10&st=2021-01-18T11%3A29%3A36Z&se=2021-01-19T11%3A59%3A36Z&sr=c&sp=rcwdl&sig=BXbsiVZ6ZogeWfyr3u2PgPVdeLlMMPOqvsh3%2BDEwnq4%3D"
- newBlob.beginCopyFromURL(encodeUrl + sas).then(
- res => {
- r(200)
- },
- err => {
- j(500)
- }
- )
- })
- }
- /**
- * 复制‘文件夹’
- * @param {string} targetFolder eg:'exam/评测id/paper/8b94c6b6-2572-41e5-89b9-a82fcf13891e/' 注意开头不加‘/’, 结尾需要加‘/’
- * @param {string} sourceFolder eg:'paper/JEFF組卷測試01'
- * @param {BlobTool} blobTool 非必传参数, 当目标文件和源文件不在同一个容器的时候,需要传源文件容器初始化的BlobTool
- */
- copyFolder(targetFolder, sourceFolder, blobTool) {
- return new Promise(async (r, j) => {
- let blobs = undefined
- let sasString = ''
- if (blobTool) {
- blobs = await blobTool.listBlob({
- prefix: sourceFolder + '/'
- }, false)
- sasString = blobTool.sasString
- } else {
- blobs = await this.listBlob({
- prefix: sourceFolder + '/'
- }, false)
- sasString = this.sasString
- }
- if (blobs && blobs.blobList) {
- let count = 0
- blobs.blobList.forEach(blobItem => {
- let newUrl = targetFolder + blobItem.name
- let newBlob = this.containerClient.getBlobClient(newUrl)
- let resourceUrl = encodeURI(blobItem.url)
- newBlob.beginCopyFromURL(resourceUrl + sasString).then().finally(() => {
- count++
- if (count == (blobs.blobList.length)) {
- r('复制结束后')
- }
- })
- })
- }
- })
- }
- /**
- * 判断文件是否存在
- * @param {string} filePath 文件路径 正确 'res/基础操作范例_16x9.HTE ' 错误 '/res/基础操作范例_16x9.HTE'
- * @param {object}
- * return true/false
- */
- exists(filePath, options) {
- const blockBlobClient = this.containerClient.getBlockBlobClient(filePath)
- return new Promise((r, j) => {
- blockBlobClient.exists(options).then(
- res => {
- r(res)
- },
- err => {
- j(err)
- }
- )
- })
- }
- /**
- * 计算容器的空间大小
- * return size
- */
- async getContainerSize() {
- return new Promise((r, j) => {
- this.listBlob({},false).then(
- res => {
- let totalSize = res.blobList.reduce((total, item) => {
- return total + parseInt(item.size)
- }, 0)
- this.totalSize = totalSize
- r(totalSize)
- },
- err => {
- j(err)
- }
- )
- })
- }
- /**
- * 判断容器的空间是否已满
- * @param {string} scope 校本/个人 school / private
- * return true/false
- */
- async isContainerFull() {
- return new Promise(async (r, j) => {
- try {
- let size = await this.getContainerSize()
- if (size > this.blobSpace) {
- r(true)
- } else {
- r(false)
- }
- } catch (e) {
- j('容器空间判断失败!')
- }
- })
- }
- /**
- * 计算指定文件夹的空间大小,如果不传文件夹,则查询整个容器空间
- * @param {string}
- * @param {object}
- * return true/false
- */
- getSize(option) {
- return new Promise((r, j) => {
- this.listBlob(option,false).then(
- res => {
- let sizeInfo = {
- total: 0, //所有
- image: 0, //内容模块图片
- res: 0, //内容模块教材
- video: 0, //内容模块视频
- audio: 0, //内容模块音频
- doc: 0, //内容模块文档
- other: 0, //内容模块其他文件
- data: 0 //除了内容模块之外的文件
- }
- let length = res.blobList.reduce((total, item) => {
- return total + parseInt(item.size)
- }, 0)
- sizeInfo.total = length
- for (let item of res.blobList) {
- let folder = item.blob.substring(1, JsFn.findChartIndex(item.blob, '/', 1))
- switch (folder) {
- case 'res':
- sizeInfo.res += item.size
- break
- case 'image':
- sizeInfo.image += item.size
- break
- case 'video':
- sizeInfo.video += item.size
- break
- case 'audio':
- sizeInfo.audio += item.size
- break
- case 'doc':
- sizeInfo.doc += item.size
- break
- case 'other':
- sizeInfo.other += item.size
- break
- default:
- sizeInfo.data += item.size
- break
- }
- }
- r(sizeInfo)
- },
- err => {
- j(err)
- }
- )
- })
- }
- }
|