| @@ -4,15 +4,22 @@ import com.ruoyi.common.core.controller.BaseController; | |||||
| import com.ruoyi.common.core.domain.AjaxResult; | import com.ruoyi.common.core.domain.AjaxResult; | ||||
| import com.ruoyi.file.model.FileModel; | import com.ruoyi.file.model.FileModel; | ||||
| import com.ruoyi.file.model.FileSystemFilter; | 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 com.ruoyi.file.service.FileSystemService; | ||||
| import io.swagger.annotations.Api; | import io.swagger.annotations.Api; | ||||
| import io.swagger.annotations.ApiOperation; | import io.swagger.annotations.ApiOperation; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.security.access.prepost.PreAuthorize; | import org.springframework.security.access.prepost.PreAuthorize; | ||||
| import org.springframework.web.bind.annotation.GetMapping; | 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.RequestMapping; | ||||
| import org.springframework.web.bind.annotation.RequestParam; | |||||
| import org.springframework.web.bind.annotation.RestController; | import org.springframework.web.bind.annotation.RestController; | ||||
| import org.springframework.web.multipart.MultipartFile; | |||||
| import javax.validation.Valid; | |||||
| import java.util.List; | import java.util.List; | ||||
| /** | /** | ||||
| @@ -42,4 +49,16 @@ public class FileSystemController extends BaseController | |||||
| return AjaxResult.success(list); | 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 fileName 文件名称 | ||||
| * @param delete 是否删除 | * @param delete 是否删除 | ||||
| */ | */ | ||||
| @GetMapping("/download") | |||||
| //@GetMapping("/download") | |||||
| public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) | public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) | ||||
| { | { | ||||
| try | try | ||||
| @@ -95,7 +95,7 @@ public final class PathUtils | |||||
| else if(i == part.length() - 1) | else if(i == part.length() - 1) | ||||
| return part; | return part; | ||||
| else | else | ||||
| return part.substring(0, i); | |||||
| return part.substring(0, i + 1); | |||||
| } | } | ||||
| private PathUtils() {} | private PathUtils() {} | ||||
| @@ -6,10 +6,12 @@ import com.ruoyi.file.enums.FileType; | |||||
| import lombok.Data; | import lombok.Data; | ||||
| import java.io.File; | import java.io.File; | ||||
| import java.util.Comparator; | |||||
| import java.util.List; | import java.util.List; | ||||
| import java.util.Objects; | |||||
| @Data | @Data | ||||
| public final class FileModel | |||||
| public final class FileModel implements Comparator | |||||
| { | { | ||||
| private String fileName; | private String fileName; | ||||
| private String filePath; | private String filePath; | ||||
| @@ -42,4 +44,14 @@ public final class FileModel | |||||
| filePath = PathUtils.normalizeSlash(filePath); | filePath = PathUtils.normalizeSlash(filePath); | ||||
| type = FileType.FileType(file); | 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; | package com.ruoyi.file.service; | ||||
| import cn.hutool.core.io.FileUtil; | |||||
| import com.ruoyi.common.exception.ASSERT; | import com.ruoyi.common.exception.ASSERT; | ||||
| import com.ruoyi.common.utils.StringUtils; | import com.ruoyi.common.utils.StringUtils; | ||||
| import com.ruoyi.common.utils.file.PathUtils; | |||||
| import com.ruoyi.file.model.FileSystemFilter; | import com.ruoyi.file.model.FileSystemFilter; | ||||
| import com.ruoyi.file.model.FileModel; | 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.stereotype.Service; | ||||
| import org.springframework.web.multipart.MultipartFile; | |||||
| import java.io.File; | import java.io.File; | ||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.Comparator; | |||||
| import java.util.List; | import java.util.List; | ||||
| @Service | @Service | ||||
| @@ -36,6 +42,61 @@ public class FileSystemService | |||||
| list.add(new FileModel(f)); | list.add(new FileModel(f)); | ||||
| } | } | ||||
| } | } | ||||
| list.sort(new FileModel()); | |||||
| return list; | 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('input', data.filePath); | ||||
| this.$emit('selected', data); | 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> | </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> | |||||