| @@ -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> | |||