文件分享
文件分享实现过程概览
- 用户可以将自己的文件(
单个文件或单个文件夹
的方式)以链接 + 提取码
的形式分享出去,其它用户(包括分享该文件的用户)都需要先输入提取码,才能够查看用户分享的文件
- 获取分享文件的过程:用户输入用户分享的链接,跳转到
文件分享的页面
,当前页面会立即发请求给后台,求证当前会话是否该分享输入正确过提取码
,如果输入过,则正常获取该分享的文件列表信息。如果没有输入过,那么,让用户跳转到输入提取码的页面
,然后,发请求给后台,验证提取码是否正确,如果正确,则跳转到文件分享页面。
- 用户来到文件分享页面,可以预览用户分享的文件、保存用户分享的文件(可单个保存、多个保存)
- 当前用户所分享的文件在查看文件分享页面时,可以取消分享(整个分享全部取消分享)
将文件分享出去
ShareFile.vue
- Object.assign({},data) 完成赋值
- 复制 分享链接及提取码 (使用vue-clipboard3实现)
- 可以通过
window.location.origin
获取当前的域,可参考:浏览器中location详解
<template>
<div>
<Dialog
:show="dialogConfig.show"
:title="dialogConfig.title"
:buttons="dialogConfig.buttons"
width="600px"
:showCancel="showCancel"
@close="dialogConfig.show = false"
>
<el-form
:model="formData"
:rules="rules"
ref="formDataRef"
label-width="100px"
@submit.prevent
>
<el-form-item label="文件"> {{ formData.fileName }} el-form-item>
<template v-if="showType == 0">
<el-form-item label="有效期" prop="validType">
<el-radio-group v-model="formData.validType">
<el-radio :label="0">1天el-radio>
<el-radio :label="1">7天el-radio>
<el-radio :label="2">30天el-radio>
<el-radio :label="3">永久有效el-radio>
el-radio-group>
el-form-item>
<el-form-item label="提取码" prop="codeType">
<el-radio-group v-model="formData.codeType">
<el-radio :label="0">自定义el-radio>
<el-radio :label="1">系统生成el-radio>
el-radio-group>
el-form-item>
<el-form-item prop="code" v-if="formData.codeType == 0">
<el-input
clearable
placeholder="请输入5位提取码"
v-model.trim="formData.code"
maxLength="5"
:style="{ width: '130px' }"
>el-input>
el-form-item>
template>
<template v-else>
<el-form-item label="分享连接" prop="validType">
{{ shareUrl }}{{ resultInfo.shareId }}
el-form-item>
<el-form-item label="提取码" prop="validType">
{{ resultInfo.code }}
el-form-item>
<el-form-item prop="validType">
<el-button type="primary" @click="copy">复制链接极提取码el-button>
el-form-item>
template>
el-form>
Dialog>
div>
template>
<script setup>
import useClipboard from "vue-clipboard3";
const { toClipboard } = useClipboard();
import { ref, getCurrentInstance, nextTick } from "vue";
import { useRouter } from "vue-router";
const { proxy } = getCurrentInstance();
const router = useRouter();
const shareUrl = ref(document.location.origin + "/share/");
const api = {
shareFile: "/share/shareFile",
};
const showType = ref(0);
const formData = ref({});
const formDataRef = ref();
const rules = {
validType: [{ required: true, message: "请选择有效期" }],
codeType: [{ required: true, message: "请选择提取码类型" }],
code: [
{ required: true, message: "请输入提取码" },
{ validator: proxy.Verify.shareCode, message: "提取码只能是数字字母" },
{ min: 5, message: "提取码最少5位" },
],
};
const showCancel = ref(true);
const dialogConfig = ref({
show: false,
title: "分享",
buttons: [
{
type: "primary",
text: "确定",
click: (e) => {
share();
},
},
],
});
const resultInfo = ref({});
const share = async () => {
if (Object.keys(resultInfo.value).length > 0) {
dialogConfig.value.show = false;
return;
}
formDataRef.value.validate(async (valid) => {
if (!valid) {
return;
}
let params = {};
Object.assign(params, formData.value);
let result = await proxy.Request({
url: api.shareFile,
params: params,
});
if (!result) {
return;
}
showType.value = 1;
resultInfo.value = result.data;
dialogConfig.value.buttons[0].text = "关闭";
showCancel.value = false;
});
};
const show = (data) => {
showCancel.value = true;
dialogConfig.value.show = true;
showType.value = 0;
resultInfo.value = {};
nextTick(() => {
formDataRef.value.resetFields();
formData.value = Object.assign({}, data);
});
};
defineExpose({ show });
const copy = async () => {
await toClipboard(
`链接:${shareUrl.value}${resultInfo.value.shareId} 提取码: ${resultInfo.value.code}`
);
proxy.Message.success("复制成功");
};
script>
<style lang="scss" scoped>style>
用户已分享文件列表
Share.vue
- 使用@import引入css,如:
@import "@/assets/file.list.scss";
- vue-clipboard3实现文本内容复制
<template>
<div>
<div class="top">
<el-button
type="primary"
:disabled="selectIdList.length == 0"
@click="cancelShareBatch">
<span class="iconfont icon-cancel">span>取消分享
el-button>
div>
<div class="file-list">
<Table
:columns="columns"
:showPagination="true"
:dataSource="tableData"
:fetch="loadDataList"
:options="tableOptions"
@rowSelected="rowSelected"
>
<template #fileName="{ index, row }">
<div
class="file-item"
@mouseenter="showOp(row)"
@mouseleave="cancelShowOp(row)">
<template v-if="(row.fileType == 3 || row.fileType == 1) && row.status !== 0 ">
<icon :cover="row.fileCover">icon>
template>
<template v-else>
<icon v-if="row.folderType == 0" :fileType="row.fileType">icon>
<icon v-if="row.folderType == 1" :fileType="0">icon>
template>
<span
class="file-name"
v-if="!row.showRename"
:title="row.fileName">
<span>{{ row.fileName }}span>
span>
<span class="op">
<template v-if="row.showOp && row.fileId">
<span class="iconfont icon-link" @click="copy(row)">复制链接span>
<span class="iconfont icon-cancel" @click="cancelShare(row)">取消分享span>
template>
span>
div>
template>
<template #expireTime="{ index, row }">
{{ row.validType == 3 ? "永久" : row.expireTime }}
template>
Table>
div>
div>
template>
<script setup>
import useClipboard from "vue-clipboard3";
const { toClipboard } = useClipboard();
import { ref, reactive, getCurrentInstance, watch } from "vue";
import { useRouter, useRoute } from "vue-router";
const { proxy } = getCurrentInstance();
const router = useRouter();
const route = useRoute();
const api = {
loadDataList: "/share/loadShareList",
cancelShare: "/share/cancelShare",
};
const shareUrl = ref(document.location.origin + "/share/");
const columns = [
{
label: "文件名",
prop: "fileName",
scopedSlots: "fileName",
},
{
label: "分享时间",
prop: "shareTime",
width: 200,
},
{
label: "失效时间",
prop: "expireTime",
scopedSlots: "expireTime",
width: 200,
},
{
label: "浏览次数",
prop: "showCount",
width: 200,
},
];
const search = () => {
showLoading.value = true;
loadDataList();
};
const tableData = ref({});
const tableOptions = {
extHeight: 20,
selectType: "checkbox",
};
const loadDataList = async () => {
let params = {
pageNo: tableData.value.pageNo,
pageSize: tableData.value.pageSize,
};
if (params.category !== "all") {
delete params.filePid;
}
let result = await proxy.Request({
url: api.loadDataList,
params,
});
if (!result) {
return;
}
tableData.value = result.data;
};
const showOp = (row) => {
tableData.value.list.forEach((element) => {
element.showOp = false;
});
row.showOp = true;
};
const cancelShowOp = (row) => {
row.showOp = false;
};
const copy = async (data) => {
await toClipboard(`链接:${shareUrl.value}${data.shareId} 提取码: ${data.code}`);
proxy.Message.success("复制成功");
};
const selectIdList = ref([]);
const rowSelected = (rows) => {
selectIdList.value = [];
rows.forEach((item) => {
selectIdList.value.push(item.shareId);
});
};
const cancelShareIdList = ref([]);
const cancelShareBatch = () => {
if (selectIdList.value.length == 0) {
return;
}
cancelShareIdList.value = selectIdList.value;
cancelShareDone();
};
const cancelShare = (row) => {
cancelShareIdList.value = [row.shareId];
cancelShareDone();
};
const cancelShareDone = async () => {
proxy.Confirm(`你确定要取消分享吗?`, async () => {
let result = await proxy.Request({
url: api.cancelShare,
params: {
shareIds: cancelShareIdList.value.join(","),
},
});
if (!result) {
return;
}
proxy.Message.success("取消分享成功");
loadDataList();
});
};
script>
<style lang="scss" scoped>
@import "@/assets/file.list.scss";
.file-list {
margin-top: 10px;
.file-item {
.file-name {
span {
&:hover {
color: #494944;
}
}
}
.op {
width: 170px;
}
}
}
style>
用户输入分享链接,进入验证页面
- 其它用户拿到链接之后,将分享链接输入到地址栏,但是,用户第一次输入的话,肯定没有验证过(会话中,没有此分享记录),因此,要跳转到分享验证页面,验证通过之后,该分享记录会存入此会话,当用户再一次进行此分享页面,就会从会话中检测到有此分享记录,此时就不需要再次验证了。
ShareCheck.vue
- 背景图片设置,可参考:CSS背景background设置
<template>
<div class="share">
<div class="body-content">
<div class="logo">
<span class="iconfont icon-pan">span>
<span class="name">Easy云盘span>
div>
<div class="code-panel">
<div class="file-info">
<div class="avatar">
<Avatar
:userId="shareInfo.userId"
:avatar="shareInfo.avatar"
:width="50"
>Avatar>
div>
<div class="share-info">
<div class="user-info">
<span class="nick-name">{{ shareInfo.nickName }} span>
<span class="share-time">分享于 {{ shareInfo.shareTime }}span>
div>
<div class="file-name">分享文件:{{ shareInfo.fileName }}div>
div>
div>
<div class="code-body">
<div class="tips">请输入提取码:div>
<div class="input-area">
<el-form
:model="formData"
:rules="rules"
ref="formDataRef"
:maxLength="5"
@submit.prevent
>
<el-form-item prop="code">
<el-input
class="input"
v-model="formData.code"
@keyup.enter="checkShare"
>el-input>
<el-button type="primary" @click="checkShare"
>提取文件el-button
>
el-form-item>
el-form>
div>
div>
div>
div>
div>
template>
<script setup>
import { ref, reactive, getCurrentInstance, nextTick, watch } from "vue";
import { useRouter, useRoute } from "vue-router";
const { proxy } = getCurrentInstance();
const router = useRouter();
const route = useRoute();
const api = {
getShareInfo: "/showShare/getShareInfo",
checkShareCode: "/showShare/checkShareCode",
};
const shareId = route.params.shareId;
const shareInfo = ref({});
const getShareInfo = async () => {
let result = await proxy.Request({
url: api.getShareInfo,
params: {
shareId,
},
});
if (!result) {
return;
}
shareInfo.value = result.data;
};
getShareInfo();
const formData = ref({});
const formDataRef = ref();
const rules = {
code: [
{ required: true, message: "请输入提取码" },
{ min: 5, message: "提取码为5位" },
{ max: 5, message: "提取码为5位" },
],
};
const checkShare = async () => {
formDataRef.value.validate(async (valid) => {
if (!valid) {
return;
}
let result = await proxy.Request({
url: api.checkShareCode,
params: {
shareId: shareId,
code: formData.value.code,
},
});
if (!result) {
return;
}
router.push(`/share/${shareId}`);
});
};
script>
<style lang="scss" scoped>
.share {
height: calc(100vh);
background: url("../../assets/share_bg.png");
background-repeat: repeat-x;
background-position: 0 bottom;
background-color: #eef2f6;
display: flex;
justify-content: center;
.body-content {
margin-top: calc(100vh / 5);
width: 500px;
.logo {
display: flex;
align-items: center;
justify-content: center;
.icon-pan {
font-size: 60px;
color: #409eff;
}
.name {
font-weight: bold;
margin-left: 5px;
font-size: 25px;
color: #409eff;
}
}
.code-panel {
margin-top: 20px;
background: #fff;
border-radius: 5px;
overflow: hidden;
box-shadow: 0 0 7px 1px #5757574f;
.file-info {
padding: 10px 20px;
background: #409eff;
color: #fff;
display: flex;
align-items: center;
.avatar {
margin-right: 5px;
}
.share-info {
.user-info {
display: flex;
align-items: center;
.nick-name {
font-size: 15px;
}
.share-time {
margin-left: 20px;
font-size: 12px;
}
}
.file-name {
margin-top: 10px;
font-size: 12px;
}
}
}
.code-body {
padding: 30px 20px 60px 20px;
.tips {
font-weight: bold;
}
.input-area {
margin-top: 10px;
.input {
flex: 1;
margin-right: 10px;
}
}
}
}
}
}
style>
验证成功后,进入分享页面
- 进入分享页面,首先仍然要请求接口,判断当前会话是否已经输入当前分享的提取码,如果没有输入,则跳转到验证页面
Share.vue
<template>
<div class="share">
<div class="header">
<div class="header-content">
<div class="logo" @click="jump">
<span class="iconfont icon-pan">span>
<span class="name">Easy云盘span>
div>
div>
div>
<div class="share-body">
<template v-if="Object.keys(shareInfo).length == 0">
<div
v-loading="Object.keys(shareInfo).length == 0"
class="loading"
>div>
template>
<template v-else>
<div class="share-panel">
<div class="share-user-info">
<div class="avatar">
<Avatar
:userId="shareInfo.userId"
:avatar="shareInfo.avatar"
:width="50"
>Avatar>
div>
<div class="share-info">
<div class="user-info">
<span class="nick-name">{{ shareInfo.nickName }} span>
<span class="share-time">分享于 {{ shareInfo.shareTime }}span>
div>
<div class="file-name">分享文件:{{ shareInfo.fileName }}div>
div>
div>
<div class="share-op-btn">
<el-button
type="primary"
v-if="shareInfo.currentUser"
@click="cancelShare"
><span class="iconfont icon-cancel">span>取消分享el-button
>
<el-button
v-else
type="primary"
:disabled="selectFileIdList.length == 0"
@click="save2MyPan"
><span class="iconfont icon-import">span
>保存到我的网盘el-button
>
div>
div>
<Navigation
ref="navigationRef"
@navChange="navChange"
:shareId="shareId"
>Navigation>
<div class="file-list">
<Table
:columns="columns"
:showPagination="true"
:dataSource="tableData"
:fetch="loadDataList"
:initFetch="false"
:options="tableOptions"
:showPageSize="false"
@rowSelected="rowSelected"
>
<template #fileName="{ index, row }">
<div
class="file-item"
@mouseenter="showOp(row)"
@mouseleave="cancelShowOp(row)"
>
<template
v-if="
(row.fileType == 3 || row.fileType == 1) && row.status !== 0
"
>
<icon :cover="row.fileCover">icon>
template>
<template v-else>
<icon
v-if="row.folderType == 0"
:fileType="row.fileType"
>icon>
<icon v-if="row.folderType == 1" :fileType="0">icon>
template>
<span class="file-name" :title="row.fileName">
<span @click="preview(row)">{{ row.fileName }}span>
span>
<span class="op">
<span
v-if="row.folderType == 0"
class="iconfont icon-download"
@click="download(row.fileId)"
>下载span
>
<template v-if="row.showOp && !shareInfo.currentUser">
<span
class="iconfont icon-import"
@click="save2MyPanSingle(row)"
>保存到我的网盘span
>
template>
span>
div>
template>
<template #fileSize="{ index, row }">
<span v-if="row.fileSize">
{{ proxy.Utils.sizeToStr(row.fileSize) }}span
>
template>
Table>
div>
template>
<FolderSelect
ref="folderSelectRef"
@folderSelect="save2MyPanDone"
>FolderSelect>
<Preview ref="previewRef"> Preview>
div>
div>
template>
<script setup>
import { ref, reactive, getCurrentInstance, watch } from "vue";
import { useRouter, useRoute } from "vue-router";
const { proxy } = getCurrentInstance();
const router = useRouter();
const route = useRoute();
const api = {
getShareLoginInfo: "/showShare/getShareLoginInfo",
loadFileList: "/showShare/loadFileList",
createDownloadUrl: "/showShare/createDownloadUrl",
download: "/api/showShare/download",
cancelShare: "/share/cancelShare",
saveShare: "/showShare/saveShare",
};
const shareId = route.params.shareId;
const shareInfo = ref({});
const getShareInfo = async () => {
let result = await proxy.Request({
url: api.getShareLoginInfo,
showLoading: false,
params: {
shareId,
},
});
if (!result) {
return;
}
if (result.data == null) {
router.push("/shareCheck/" + shareId);
return;
}
shareInfo.value = result.data;
};
getShareInfo();
const columns = [
{
label: "文件名",
prop: "fileName",
scopedSlots: "fileName",
},
{
label: "修改时间",
prop: "lastUpdateTime",
width: 200,
},
{
label: "大小",
prop: "fileSize",
scopedSlots: "fileSize",
width: 200,
},
];
const tableData = ref({});
const tableOptions = {
extHeight: 80,
selectType: "checkbox",
};
const loadDataList = async () => {
let params = {
pageNo: tableData.value.pageNo,
pageSize: tableData.value.pageSize,
shareId: shareId,
filePid: currentFolder.value.fileId,
};
let result = await proxy.Request({
url: api.loadFileList,
params,
});
if (!result) {
return;
}
tableData.value = result.data;
};
const showOp = (row) => {
tableData.value.list.forEach((element) => {
element.showOp = false;
});
row.showOp = true;
};
const cancelShowOp = (row) => {
row.showOp = false;
};
const selectFileIdList = ref([]);
const rowSelected = (rows) => {
selectFileIdList.value = [];
rows.forEach((item) => {
selectFileIdList.value.push(item.fileId);
});
};
const currentFolder = ref({ fileId: 0 });
const navChange = (data) => {
const { curFolder } = data;
currentFolder.value = curFolder;
loadDataList();
};
const previewRef = ref();
const navigationRef = ref();
const preview = (data) => {
if (data.folderType == 1) {
navigationRef.value.openFolder(data);
return;
}
data.shareId = shareId;
previewRef.value.showPreview(data, 2);
};
const download = async (fileId) => {
let result = await proxy.Request({
url: api.createDownloadUrl + "/" + shareId + "/" + fileId,
});
if (!result) {
return;
}
window.location.href = api.download + "/" + result.data;
};
const folderSelectRef = ref();
const save2MyPanFileIdArray = [];
const save2MyPan = () => {
if (selectFileIdList.value.length == 0) {
return;
}
if (!proxy.VueCookies.get("userInfo")) {
router.push("/login?redirectUrl=" + route.path);
return;
}
save2MyPanFileIdArray.values = selectFileIdList.value;
folderSelectRef.value.showFolderDialog();
};
const save2MyPanSingle = (row) => {
if (!proxy.VueCookies.get("userInfo")) {
router.push("/login?redirectUrl=" + route.path);
return;
}
save2MyPanFileIdArray.values = [row.fileId];
folderSelectRef.value.showFolderDialog();
};
const save2MyPanDone = async (folderId) => {
let result = await proxy.Request({
url: api.saveShare,
params: {
shareId: shareId,
shareFileIds: save2MyPanFileIdArray.values.join(","),
myFolderId: folderId,
},
});
if (!result) {
return;
}
loadDataList();
proxy.Message.success("保存成功");
folderSelectRef.value.close();
};
const cancelShare = () => {
proxy.Confirm(`你确定要取消分享吗?`, async () => {
let result = await proxy.Request({
url: api.cancelShare,
params: {
shareIds: shareId,
},
});
if (!result) {
return;
}
proxy.Message.success("取消分享成功");
router.push("/");
});
};
const jump = () => {
router.push("/");
};
script>
<style lang="scss" scoped>
@import "@/assets/file.list.scss";
.header {
width: 100%;
position: fixed;
background: #0c95f7;
height: 50px;
.header-content {
width: 70%;
margin: 0px auto;
color: #fff;
line-height: 50px;
.logo {
display: flex;
align-items: center;
cursor: pointer;
.icon-pan {
font-size: 40px;
}
.name {
font-weight: bold;
margin-left: 5px;
font-size: 25px;
}
}
}
}
.share-body {
width: 70%;
margin: 0px auto;
padding-top: 50px;
.loading {
height: calc(100vh / 2);
width: 100%;
}
.share-panel {
margin-top: 20px;
display: flex;
justify-content: space-around;
border-bottom: 1px solid #ddd;
padding-bottom: 10px;
.share-user-info {
flex: 1;
display: flex;
align-items: center;
.avatar {
margin-right: 5px;
}
.share-info {
.user-info {
display: flex;
align-items: center;
.nick-name {
font-size: 15px;
}
.share-time {
margin-left: 20px;
font-size: 12px;
}
}
.file-name {
margin-top: 10px;
font-size: 12px;
}
}
}
}
}
.file-list {
margin-top: 10px;
.file-item {
.op {
width: 170px;
}
}
}
style>
WebShareController#loadFileList接口
- 当用户验证成功之后,来到分享页面,需要查询到当前分享的文件fileId,这个fileId可能是一个文件,也可能是一个文件夹。如果是个文件夹,当进入这个文件夹时,此时导航也要进入此层级,导航进入后,根据路由path获取导航信息,然后,通知父组件加载这个点击的文件夹的文件和文件夹。父组件则需要以这个文件夹为filePid查询这个filePid下的下一级文件和文件夹,注意此时的后端,不能直接把前端传过来的filePid直接使用,因为这个接口是开放的(即使用户不登录,也可以访问),所以需要对这个filePid做校验,需要验证这个filePid是这个分享关联的fileId(或这个fileId的子级文件夹),以避免数据安全问题。
@RestController("webShareController")
@RequestMapping("/showShare")
public class WebShareController extends CommonFileController {
@RequestMapping("/loadFileList")
@GlobalInterceptor(checkLogin = false, checkParams = true)
public ResponseVO loadFileList(HttpSession session,
@VerifyParam(required = true) String shareId, String filePid) {
SessionShareDto shareSessionDto = checkShare(session, shareId);
FileInfoQuery query = new FileInfoQuery();
if (!StringTools.isEmpty(filePid) && !Constants.ZERO_STR.equals(filePid)) {
fileInfoService.checkRootFilePid(shareSessionDto.getFileId(), shareSessionDto.getShareUserId(), filePid);
query.setFilePid(filePid);
} else {
query.setFileId(shareSessionDto.getFileId());
}
query.setUserId(shareSessionDto.getShareUserId());
query.setOrderBy("last_update_time desc");
query.setDelFlag(FileDelFlagEnums.USING.getFlag());
PaginationResultVO resultVO = fileInfoService.findListByPage(query);
return getSuccessResponseVO(convert2PaginationVO(resultVO, FileInfoVO.class));
}
}
FileInfoServiceImpl#checkRootFilePid 递归校验
@Service("fileInfoService")
public class FileInfoServiceImpl implements FileInfoService {
@Override
public void checkRootFilePid(String rootFilePid, String userId, String fileId) {
if (StringTools.isEmpty(fileId)) {
throw new BusinessException(ResponseCodeEnum.CODE_600);
}
if (rootFilePid.equals(fileId)) {
return;
}
checkFilePid(rootFilePid, fileId, userId);
}
private void checkFilePid(String rootFilePid, String fileId, String userId) {
FileInfo fileInfo = this.fileInfoMapper.selectByFileIdAndUserId(fileId, userId);
if (fileInfo == null) {
throw new BusinessException(ResponseCodeEnum.CODE_600);
}
if (Constants.ZERO_STR.equals(fileInfo.getFilePid())) {
throw new BusinessException(ResponseCodeEnum.CODE_600);
}
if (fileInfo.getFilePid().equals(rootFilePid)) {
return;
}
checkFilePid(rootFilePid, fileInfo.getFilePid(), userId);
}
}
WebShareController#saveShare 接口
@RestController("webShareController")
@RequestMapping("/showShare")
public class WebShareController extends CommonFileController {
@RequestMapping("/saveShare")
@GlobalInterceptor(checkParams = true)
public ResponseVO saveShare(HttpSession session,
@VerifyParam(required = true) String shareId,
@VerifyParam(required = true) String shareFileIds,
@VerifyParam(required = true) String myFolderId) {
SessionShareDto shareSessionDto = checkShare(session, shareId);
SessionWebUserDto webUserDto = getUserInfoFromSession(session);
if (shareSessionDto.getShareUserId().equals(webUserDto.getUserId())) {
throw new BusinessException("自己分享的文件无法保存到自己的网盘");
}
fileInfoService.saveShare(shareSessionDto.getFileId(), shareFileIds, myFolderId, shareSessionDto.getShareUserId(), webUserDto.getUserId());
return getSuccessResponseVO(null);
}
}
FileInfoServiceImpl #saveShare 保存别人分享的文件 递归子文件夹
@Service("fileInfoService")
public class FileInfoServiceImpl implements FileInfoService {
@Override
public void saveShare(String shareRootFilePid, String shareFileIds, String myFolderId, String shareUserId, String cureentUserId) {
String[] shareFileIdArray = shareFileIds.split(",");
FileInfoQuery fileInfoQuery = new FileInfoQuery();
fileInfoQuery.setUserId(cureentUserId);
fileInfoQuery.setFilePid(myFolderId);
List<FileInfo> currentFileList = this.fileInfoMapper.selectList(fileInfoQuery);
Map<String, FileInfo> currentFileMap = currentFileList.stream().collect(Collectors.toMap(FileInfo::getFileName, Function.identity(), (file1, file2) -> file2));
fileInfoQuery = new FileInfoQuery();
fileInfoQuery.setUserId(shareUserId);
fileInfoQuery.setFileIdArray(shareFileIdArray);
List<FileInfo> shareFileList = this.fileInfoMapper.selectList(fileInfoQuery);
List<FileInfo> copyFileList = new ArrayList<>();
Date curDate = new Date();
for (FileInfo item : shareFileList) {
FileInfo haveFile = currentFileMap.get(item.getFileName());
if (haveFile != null) {
item.setFileName(StringTools.rename(item.getFileName()));
}
findAllSubFile(copyFileList, item, shareUserId, cureentUserId, curDate, myFolderId);
}
System.out.println(copyFileList.size());
this.fileInfoMapper.insertBatch(copyFileList);
}
private void findAllSubFile(List<FileInfo> copyFileList, FileInfo fileInfo, String sourceUserId, String currentUserId, Date curDate, String newFilePid) {
String sourceFileId = fileInfo.getFileId();
fileInfo.setCreateTime(curDate);
fileInfo.setLastUpdateTime(curDate);
fileInfo.setFilePid(newFilePid);
fileInfo.setUserId(currentUserId);
String newFileId = StringTools.getRandomString(Constants.LENGTH_10);
fileInfo.setFileId(newFileId);
copyFileList.add(fileInfo);
if (FileFolderTypeEnums.FOLDER.getType().equals(fileInfo.getFolderType())) {
FileInfoQuery query = new FileInfoQuery();
query.setFilePid(sourceFileId);
query.setUserId(sourceUserId);
List<FileInfo> sourceFileList = this.fileInfoMapper.selectList(query);
for (FileInfo item : sourceFileList) {
findAllSubFile(copyFileList, item, sourceUserId, currentUserId, curDate, newFileId);
}
}
}
}