file sharing
Overview of file sharing implementation process
Users can share their own files ( 单个文件或单个文件夹
in a way) in the 链接 + 提取码
form of , and other users (including users who share the file) need to enter the extraction code before they can view the files shared by the user
The process of obtaining shared files: the user enters the link shared by the user and jumps to it 文件分享的页面
. The current page will immediately send a request to the background for verification 当前会话是否该分享输入正确过提取码
. If the link has been entered, the shared file list information will be obtained normally. If it has not been entered, let the user jump to it 输入提取码的页面
, and then send a request to the background to verify whether the extraction code is correct, and if it is correct, jump to the file sharing page.
When the user comes to the file sharing page, he can preview the files shared by the user and save the files shared by the user (single saving or multiple saving is possible)
Files shared by the current user can be unshared when viewing the file sharing page (the entire share is unshared)
Share files
ShareFile.vue
Object.assign({},data) completes assignment
Copy the sharing link and extraction code (implemented using vue-clipboard3)
You can window.location.origin
get the current domain, please refer to: Detailed explanation of location in the browser
< 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>
List of files shared by the user
Share.vue
Use @import to introduce css, such as: @import "@/assets/file.list.scss";
vue-clipboard3 implements text content copying
< 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>
The user enters the sharing link and enters the verification page
After other users get the link, they enter the sharing link into the address bar. However, if the user enters it for the first time, it must not be verified (there is no such sharing record in the session), so jump to the sharing verification page and pass the verification Afterwards, the sharing record will be stored in the session, and when the user accesses the sharing page again, the sharing record will be detected from the session, and there is no need to verify again at this time.
ShareCheck.vue
< 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>
After successful verification, enter the sharing page
Enter the sharing page, first still need to request the interface to judge whether the current session has entered the current shared extraction code, if not, jump to the verification page
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 interface
When the user is successfully authenticated and comes to the sharing page, he needs to query the fileId of the currently shared file. This fileId may be a file or a folder. If it is a folder, when entering this folder, the navigation also enters this level at this time. After the navigation is entered, the navigation information is obtained according to the routing path, and then the parent component is notified to load the files and folders of the clicked folder. The parent component needs to use this folder as filePid to query the next-level files and folders under this filePid. Note that the backend at this time cannot directly use the filePid passed from the frontend, because this interface is open (even if the user You can also access it without logging in), so you need to verify this filePid, and you need to verify that this filePid is the fileId associated with this share (or the subfolder of this fileId), in order to avoid data security issues.
@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 recursive verification
@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 interface
Save files shared by other users to your own space
@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 Save files shared by others in recursive subfolders
@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) ;
}
}
}
}