前言
上一节,已经初始化了整个项目。这一节我直接跳过react-redux和react-router-dom的配置等等,啥也不说了直接上代码
OSS小文件存储
- 新建OSS controller
const OSS = window.require('ali-oss');
const fs = window.require('fs');
// ali-oss sdk的插件,简化删除文件和文件夹等的操作
const ExtraOSS = window.require('ali-oss-extra');
// 一个普通的统一消息的类
const {SuccessModel, ErrorModel, FailedModel} = require('./Model');
const Store = window.require('electron-store');
import axios from 'axios';
const settingsStore = new Store({
name: 'Settings'
});
// 读取配置(配置在main.js设置,可读取环境变量)
const endpoint = settingsStore.get('endpoint');
const access = settingsStore.get('accessKey');
const secret = settingsStore.get('secretKey');
const bucket = settingsStore.get('bucketName');
class AliOSS {
constructor({accessKeyId, accessKeySecret, bucket, endpoint}) {
this.ak = accessKeyId;
this.sk = accessKeySecret;
this.bucket = bucket;
this.endPoint = endpoint;
this.store = OSS({endpoint, accessKeyId, accessKeySecret, bucket});
this.client = new OSS({endpoint, accessKeyId, accessKeySecret, bucket});
}
/**
* 获取文件
* @param prefix 文件夹前缀
* @param delimiter 是否包含子文件夹的读取
* @return {Promise<*>}
*/
async getObjectsFromOss(prefix, delimiter) {
if (delimiter) {
return await this.client.list({prefix, delimiter})
} else {
return await this.client.list({prefix})
}
}
/**
* 上传文件
* @param key 云空间的名字
* @param localPath 本地文件路径
* @param options
*/
async uploadFile(key, localPath, options = {type: '.md'}) {
// object表示上传到OSS的Object名称,localPath表示本地文件或者文件路径
let {res} = await this.client.put(`${key}${options.type}`, localPath);
if (res.status === 200) {
return new SuccessModel({
msg: '上传成功'
})
} else {
return new FailedModel({
msg: '上传失败'
})
}
}
/**
* 上传文件
* @param key 云空间的名字
* @param buffer 文件内容的buffer
* @param options
*/
async uploadFile_v2(key, buffer, options = {type: '.md'}) {
// object表示上传到OSS的Object名称,localPath表示本地文件或者文件路径
let {res} = await this.client.putStream(`${key}${options.type}`, buffer);
console.log(buffer)
if (res.status === 200) {
return new SuccessModel({
msg: '上传成功'
})
} else {
return new FailedModel({
msg: '上传失败'
})
}
}
/**
* 判断文件是否存在于云端
* @param key 文件名
* @return {Promise<*>}
*/
async isExistObject(key) {
try {
let {res} = await this.client.getObjectMeta(key);
return new SuccessModel({
msg: '文件存在于云端',
data: {
lastModified: +new Date(res.headers['last-modified'])
}
})
} catch (e) {
return new FailedModel({
msg: '文件不存在于云端'
})
}
}
async downloadFile_v2(key, filePath) {
try {
let {res, stream} = await this.client.getStream(key);
if (res.status === 200) {
let writeStream = fs.createWriteStream(filePath);
stream.pipe(writeStream);
return new SuccessModel({
msg: '下载成功'
});
} else {
return new FailedModel({
msg: '下载失败'
})
}
} catch (e) {
return new ErrorModel({
msg: '阿里云OSS服务器异常'
})
}
}
/**
* 取得img的路径
* @param key 文件名
* @return {Promise<*>}
*/
async getImgUrl(key) {
try {
return this.client.generateObjectUrl(key);
} catch (e) {
}
}
/**
* 创建文件夹,以/结尾
* @param key 文件名
* @return {Promise<*>}
*/
async createDir(key) {
let url = 'https://' + this.bucket + '.' + this.endPoint + '/' + key;
return axios.put(url)
}
/**
* 复制文件
* @param from
* @param to
* @return {Promise<*>}
*/
async copyFile(from, to) {
let url = 'https://' + this.bucket + '.' + this.endPoint + '/' + to;
return axios(url, {
method: 'put',
headers: {
'x-oss-copy-source': '/cloud-doc-editor/' + from
}
})
}
}
const manager = new AliOSS({
accessKeyId: access,
endpoint: endpoint,
accessKeySecret: secret,
bucket: bucket,
});
const extraManager = new OSS.default({
accessKeyId: access,
endpoint: endpoint,
accessKeySecret: secret,
bucket: bucket,
});
module.exports = {manager, extraManager};
获取当前路由下的文件夹
// 引入querystring包处理路由
const querystring = window.require('querystring');
....
// 得到searchObj:{prefix: xxx}
const searchObj = querystring.parse(location.search.slice(1));
// 将返回的数据形成固定的结构
const filterFile = (prefix, data) => {
// 文件夹
let filteredDirs = data.prefixes && data.prefixes.map(dir => {
// 等到当前的路径的文件夹名和文件夹,并且设置其为不是新文件
return {dirname: dir.slice(prefix.length, -1), dir: dir.replace(/\/$/g, ''), id: dir, isNew: false}
});
// 文件
let filteredObjects = data.objects && data.objects.map(object => {
let {lastModified, url, name, size} = object;
let extname = nodePath.extname(name);
if (extname !== '') {
let filename = name.slice(prefix.length);
return {
filename,
id: name,
serverUpdatedAt: +new Date(lastModified),
url,
extname: extname.slice(extname.indexOf('.') + 1),
size
}
}
}).filter(item => item);
return {
// 返回的是以id为键名的obj
dirs: filteredDirs ? array2Obj(filteredDirs) : {},
objects: filteredObjects ? array2Obj(filteredObjects) : {}
}
};
// 获取当前路径下的文件和文件夹
const getData = useCallback(() => {
// 登录之后才能渲染文件夹和文件
if (loginInfo && loginInfo.user) {
// 默认的根文件夹是用户id为前缀
let prefix = searchObj.prefix + '/';
// 获取当前目录的文件夹和文件
manager.getObjectsFromOss(prefix, '/')
.then(data => {
// 去掉前缀
let {objects, dirs} = filterFile(prefix, data);
// 设置redux store的中的数据
setCloudObjects(objects || {});
setCloudDir(dirs || {});
});
}
}, [searchObj]);
useEffect(() => {
getData()
}, [location]);
...
文件夹和文件双击事件
- 文件夹的双击 文件夹的双击即进入该层文件夹,获取该层文件和文件夹进行渲染
const handleDbClick = useCallback(dir => {
// 取得当前点击的文件夹dir路径,然后改变路由
let url = page + '?prefix=' + dir.dir;
history.push(url);
}, [location]);
- 文件的双击 文件的双击即下载文件
// 取得配置的文件下载路径
const downloadPath = settingsStore.get('fileDownloadPath');
const downloadFile = useCallback((file) => {
// 取得当前文件的id
const key = file.id;
// 下载路径的拼接
let filePath = downloadPath + '/' + +new Date() + '_' + file.filename;
// 下载文件到指定路径
manager.downloadFile_v2(key, filePath)
.then(data => {
if (data.code === 0) {
file.filePath = filePath;
file.date = +new Date();
// 添加当前用户的下载历史
addDownloadHistory(loginInfo.user.id, file);
message.success("下载成功")
} else {
message.error("下载失败")
}
})
.catch((err) => {
message.error("服务器异常")
})
}, [searchObj]);