@@ -4,15 +4,22 @@ import com.ruoyi.common.core.controller.BaseController; | |||
import com.ruoyi.common.core.domain.AjaxResult; | |||
import com.ruoyi.file.model.FileModel; | |||
import com.ruoyi.file.model.FileSystemFilter; | |||
import com.ruoyi.file.object.UploadResp; | |||
import com.ruoyi.file.request.PutFileReq; | |||
import com.ruoyi.file.service.FileSystemService; | |||
import io.swagger.annotations.Api; | |||
import io.swagger.annotations.ApiOperation; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.security.access.prepost.PreAuthorize; | |||
import org.springframework.web.bind.annotation.GetMapping; | |||
import org.springframework.web.bind.annotation.PathVariable; | |||
import org.springframework.web.bind.annotation.PostMapping; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import org.springframework.web.bind.annotation.RequestParam; | |||
import org.springframework.web.bind.annotation.RestController; | |||
import org.springframework.web.multipart.MultipartFile; | |||
import javax.validation.Valid; | |||
import java.util.List; | |||
/** | |||
@@ -42,4 +49,16 @@ public class FileSystemController extends BaseController | |||
return AjaxResult.success(list); | |||
} | |||
/** | |||
* 上传文件 | |||
*/ | |||
@ApiOperation("上传文件") | |||
@PreAuthorize("@ss.hasPermi('admin:fs:put')") | |||
@PostMapping("/put") | |||
public AjaxResult put(@Valid PutFileReq req, MultipartFile file) | |||
{ | |||
FileModel upload = fileSystemService.put(req, file); | |||
return AjaxResult.success(upload); | |||
} | |||
} |
@@ -48,7 +48,7 @@ public class OpenFileController | |||
* @param fileName 文件名称 | |||
* @param delete 是否删除 | |||
*/ | |||
@GetMapping("/download") | |||
//@GetMapping("/download") | |||
public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) | |||
{ | |||
try | |||
@@ -95,7 +95,7 @@ public final class PathUtils | |||
else if(i == part.length() - 1) | |||
return part; | |||
else | |||
return part.substring(0, i); | |||
return part.substring(0, i + 1); | |||
} | |||
private PathUtils() {} | |||
@@ -6,10 +6,12 @@ import com.ruoyi.file.enums.FileType; | |||
import lombok.Data; | |||
import java.io.File; | |||
import java.util.Comparator; | |||
import java.util.List; | |||
import java.util.Objects; | |||
@Data | |||
public final class FileModel | |||
public final class FileModel implements Comparator | |||
{ | |||
private String fileName; | |||
private String filePath; | |||
@@ -42,4 +44,14 @@ public final class FileModel | |||
filePath = PathUtils.normalizeSlash(filePath); | |||
type = FileType.FileType(file); | |||
} | |||
@Override | |||
public int compare(Object o1, Object o2) | |||
{ | |||
FileModel a = (FileModel)o1; | |||
FileModel b = (FileModel)o2; | |||
if(Objects.equals(a.type, b.type)) | |||
return a.fileName.compareToIgnoreCase(b.fileName); | |||
return -(a.type - b.type); | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
package com.ruoyi.file.request; | |||
import lombok.Data; | |||
import javax.validation.constraints.NotEmpty; | |||
@Data | |||
public final class PutFileReq | |||
{ | |||
@NotEmpty(message = "上传路径不能为空") | |||
private String path; | |||
private Integer ifExists; | |||
public boolean allowOverride() | |||
{ | |||
return null != ifExists && ifExists == 2; | |||
} | |||
public boolean allowRename() | |||
{ | |||
return null != ifExists && ifExists == 1; | |||
} | |||
} |
@@ -1,13 +1,19 @@ | |||
package com.ruoyi.file.service; | |||
import cn.hutool.core.io.FileUtil; | |||
import com.ruoyi.common.exception.ASSERT; | |||
import com.ruoyi.common.utils.StringUtils; | |||
import com.ruoyi.common.utils.file.PathUtils; | |||
import com.ruoyi.file.model.FileSystemFilter; | |||
import com.ruoyi.file.model.FileModel; | |||
import com.ruoyi.file.request.PutFileReq; | |||
import org.apache.commons.io.FilenameUtils; | |||
import org.springframework.stereotype.Service; | |||
import org.springframework.web.multipart.MultipartFile; | |||
import java.io.File; | |||
import java.util.ArrayList; | |||
import java.util.Comparator; | |||
import java.util.List; | |||
@Service | |||
@@ -36,6 +42,61 @@ public class FileSystemService | |||
list.add(new FileModel(f)); | |||
} | |||
} | |||
list.sort(new FileModel()); | |||
return list; | |||
} | |||
public FileModel put(PutFileReq req, MultipartFile file) | |||
{ | |||
String dst = PathUtils.appendPaths(req.getPath(), file.getOriginalFilename()); | |||
File dstFile = new File(dst); | |||
if(dstFile.exists()) | |||
{ | |||
if(dstFile.isDirectory()) | |||
throw new RuntimeException("目标文件是目录: " + dst); | |||
if(req.allowRename()) | |||
{ | |||
int i = 1; | |||
do | |||
{ | |||
String originalFilename = file.getOriginalFilename(); | |||
String extension = FilenameUtils.getExtension(originalFilename); | |||
originalFilename = PathUtils.removeExtension(originalFilename); | |||
originalFilename += "(" + i++ + ")"; | |||
if(StringUtils.isNotEmpty(extension)) | |||
originalFilename += "." + extension; | |||
dst = PathUtils.appendPaths(req.getPath(), originalFilename); | |||
dstFile = new File(dst); | |||
} while(dstFile.exists()); | |||
} | |||
else if(req.allowOverride()) | |||
{ | |||
// override | |||
} | |||
else | |||
throw new RuntimeException("目标文件已存在: " + dst); | |||
} | |||
File parentFile = dstFile.getParentFile(); | |||
if(null == parentFile) | |||
throw new RuntimeException("必须指定存储目录: " + dst); | |||
if(parentFile.exists() && !parentFile.isDirectory()) | |||
throw new RuntimeException("存储目录必须是文件夹: " + dst); | |||
if(!parentFile.exists()) | |||
{ | |||
if(!parentFile.mkdirs()) | |||
throw new RuntimeException("存储目录不存在且创建失败: " + dst); | |||
} | |||
try | |||
{ | |||
byte[] bytes = file.getBytes(); | |||
FileUtil.writeBytes(bytes, dstFile); | |||
return new FileModel(dstFile); | |||
} | |||
catch(Exception e) | |||
{ | |||
e.printStackTrace(); | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
} |
@@ -60,6 +60,17 @@ export default { | |||
this.$emit('input', data.filePath); | |||
this.$emit('selected', data); | |||
}, | |||
reload() { | |||
this.files = []; | |||
let query = { | |||
filePath: null, | |||
fileType: this.fileType, | |||
showHidden: this.showHidden, | |||
}; | |||
ls(query).then((resp) => { | |||
this.files = resp.data; | |||
}); | |||
}, | |||
} | |||
}; | |||
</script> | |||
@@ -0,0 +1,204 @@ | |||
<template> | |||
<div class="app-container"> | |||
<el-row :gutter="10" class="mb8"> | |||
<el-col :span="1.5"> | |||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="upload" v-hasPermi="['admin:fs:put']">Put</el-button> | |||
</el-col> | |||
<right-toolbar :showSearch.sync="showSearch" @queryTable="refresh"></right-toolbar> | |||
</el-row> | |||
<el-row> | |||
<el-col :span="8"> | |||
<ServerFileChooser ref="fileBrowser" v-model="form.path" :file-type="2" @selected="getFiles"/> | |||
</el-col> | |||
<el-col :span="16"> | |||
<el-table :data="files"> | |||
<el-table-column type="index" label="序号" width="50" align="center" /> | |||
<el-table-column label="文件名" align="left" header-align="center" prop="fileName" sortable/> | |||
</el-table> | |||
</el-col> | |||
</el-row> | |||
<el-dialog title="上传文件" :visible.sync="open" width="700px" append-to-body> | |||
<el-form ref="form" :model="form" label-width="100px" :rules="rules"> | |||
<el-form-item label="上传至"> | |||
<div>{{form.path}}</div> | |||
</el-form-item> | |||
<el-form-item label="如果已存在" prop="ifExists"> | |||
<el-select v-model="form.ifExists" placeholder="请选择如果已存在行为"> | |||
<el-option | |||
v-for="dict in ifExistsDict" | |||
:key="dict.value" | |||
:label="dict.label" | |||
:value="dict.value" | |||
></el-option> | |||
</el-select> | |||
</el-form-item> | |||
<el-form-item label="文件" prop="file"> | |||
<el-upload | |||
ref="upload" | |||
drag | |||
:action="uploadUrl" | |||
:auto-upload="false" | |||
:data="form" | |||
:limit="1" | |||
:multiple="false" | |||
:headers="header" | |||
:on-success="onUploadSuccess" | |||
:on-error="onUploadError" | |||
:before-upload="startUpload" | |||
> | |||
<i class="el-icon-upload"></i> | |||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> | |||
</el-upload> | |||
</el-form-item> | |||
</el-form> | |||
<div slot="footer" class="dialog-footer"> | |||
<el-button type="primary" @click="put" :loading="uploading">上 传</el-button> | |||
<el-button @click="cancel">取 消</el-button> | |||
</div> | |||
</el-dialog> | |||
</div> | |||
</template> | |||
<script> | |||
import DeptSelect from "@/components/common/DeptSelect.vue"; | |||
import ServerFileChooser from "@/components/ServerFileChooser.vue"; | |||
import FullscreenDialog from "@/components/FullscreenDialog.vue"; | |||
import ProjectState from "@/views/file/state/ProjectState.vue"; | |||
import {stateList, gc, stateMachine, dump, runtimeState} from "@/api/file/state"; | |||
import Descriptions from "@/components/common/Descriptions.vue"; | |||
import {formatFileSize} from "@/utils/file"; | |||
import {ls} from "@/api/file/filesystem"; | |||
import {getToken} from "@/utils/auth"; | |||
export default { | |||
name: "State", | |||
components: { | |||
Descriptions, | |||
ProjectState, | |||
FullscreenDialog, | |||
ServerFileChooser, | |||
DeptSelect | |||
}, | |||
dicts: ['sys_bool'], | |||
data() { | |||
return { | |||
loading: false, | |||
uploading: false, | |||
showSearch: false, | |||
// 总条数 | |||
total: 0, | |||
uploadUrl: '/api/filesystem/put', | |||
open: false, | |||
queryParams: { | |||
// 分页 | |||
pageNum: 1, | |||
pageSize: 10, | |||
// 查询排序 | |||
//orderByColumn: "id", | |||
//isAsc: "desc", | |||
// 翻译字典 | |||
//translate_dict: "1", | |||
}, | |||
form: { | |||
path: '', | |||
ifExists: 0, | |||
}, | |||
files: [], | |||
ifExistsDict: [ | |||
{label: '中止', value: 0,}, | |||
{label: '重命名', value: 1,}, | |||
{label: '覆盖', value: 2,}, | |||
], | |||
rules: { | |||
ifExists: [ | |||
{ required: true, message: "如果已存在行为不能为空", trigger: "change" } | |||
], | |||
}, | |||
header: { | |||
Authorization: "Bearer " + getToken(), | |||
}, | |||
}; | |||
}, | |||
created() { | |||
}, | |||
methods: { | |||
// 取消按钮 | |||
cancel() { | |||
this.open = false; | |||
}, | |||
// 表单重置 | |||
reset() { | |||
this.form = { | |||
path: '', | |||
ifExists: 0, | |||
}; | |||
this.$refs.form.resetField(); | |||
}, | |||
refresh() { | |||
this.$refs.fileBrowser.reload(); | |||
this.files = []; | |||
}, | |||
getFiles() { | |||
this.files = []; | |||
if(this.form.path) | |||
{ | |||
let query = { | |||
filePath: this.form.path, | |||
showHidden: true, | |||
}; | |||
this.loading = true; | |||
ls(query).then((resp) => { | |||
this.files = resp.data; | |||
}).finally(() => this.loading = false); | |||
} | |||
}, | |||
resetQuery() { | |||
this.resetForm("queryForm"); | |||
this.handleQuery(); | |||
}, | |||
fileSizeFormatter(row, col, val) { | |||
return formatFileSize(val); | |||
}, | |||
upload() { | |||
if(!this.form.path) | |||
{ | |||
this.$message.error('请先选择存储目录'); | |||
return; | |||
} | |||
this.uploading = false; | |||
this.open = true; | |||
this.$nextTick(() => { | |||
this.$refs.upload.clearFiles(); | |||
}); | |||
}, | |||
put() { | |||
this.$refs.upload.submit(); | |||
}, | |||
onUploadSuccess(response, file, fileList) { | |||
this.uploading = false; | |||
if(response.code == 200) | |||
{ | |||
this.$message.success('上传成功! 路径为: ' + response.data.filePath); | |||
this.cancel(); | |||
this.getFiles(); | |||
} | |||
else | |||
this.$message.error('上传失败: ' + response.msg); | |||
}, | |||
onUploadError(err, file, fileList) { | |||
this.uploading = false; | |||
this.$message.error('上传失败: ' + err); | |||
}, | |||
startUpload(file) { | |||
this.uploading = true; | |||
}, | |||
}, | |||
filters: { | |||
formatFileSize, | |||
}, | |||
}; | |||
</script> |