Skip to content

Commit

Permalink
新增: 文件夹上传和下载 #34
Browse files Browse the repository at this point in the history
  • Loading branch information
TheBlindM committed Dec 17, 2022
1 parent 9e3ad44 commit 6164482
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.validation.constraints.NotBlank;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.List;
Expand Down Expand Up @@ -77,8 +78,8 @@ public BaseResponse rename(@PathParam("channelId") String channelId, @FormParam(
@Path("/download/{channelId}")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public BaseResponse download(@PathParam("channelId") String channelId, @FormParam("path") String path) {
fileManagerService.download(channelId, path);
public BaseResponse download(@PathParam("channelId") String channelId, @FormParam("path") String path, @FormParam("isDirectory") Boolean isDirectory) {
fileManagerService.download(channelId, path,isDirectory);
return BaseResponse.ok();
}

Expand Down
111 changes: 101 additions & 10 deletions server/src/main/java/com/tshell/core/FileManagerService.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import cn.hutool.core.io.watch.watchers.DelayWatcher;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tshell.core.task.TaskExecutor;
import com.tshell.core.tty.TtyConnector;
Expand Down Expand Up @@ -37,12 +37,14 @@
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
Expand Down Expand Up @@ -139,12 +141,79 @@ public void upload(String channelId, UploadDTO uploadDTO) {
String dir = uploadDTO.path();
File file = FileUtil.file(filePath);
var fileName = file.getName();
String completePath = dir.endsWith(separator) ? dir + fileName : dir + separator + fileName;
if (file.isDirectory()) {
try (Stream<Path> paths = Files.walk(file.toPath())) {
paths.forEach((path -> {
File internalFile = path.toFile();
String relativePath = transformSeparators(StrUtil.subAfter(FileUtil.getCanonicalPath(internalFile), FileUtil.getAbsolutePath(file.getParentFile()), true), separator);
if (!relativePath.startsWith(separator)) {
relativePath = separator + relativePath;
}
if (internalFile.isDirectory()) {
if (!relativePath.endsWith(separator)) {
relativePath = relativePath + separator;
}
getFileManager(channelId).create(dir, relativePath, FileType.DIRECTORY);
} else {
String completePath = dir + relativePath;
doUpload(TransferInfo.buildTransferInfo(completePath, FileUtil.getCanonicalPath(internalFile), sessionId, file.getTotalSpace(), FileOperate.PUT), channelId);
}
}));
} catch (IOException e) {
throw new RuntimeException(e);
}

} else {

String completePath = dir.endsWith(separator) ? dir + fileName : dir + separator + fileName;
doUpload(TransferInfo.buildTransferInfo(completePath, filePath, sessionId, file.getTotalSpace(), FileOperate.PUT), channelId);
}

doUpload(TransferInfo.buildTransferInfo(completePath, filePath, sessionId, file.getTotalSpace(), FileOperate.PUT), channelId);
});
}


public static String separatorsToUnix(String path) {
return path != null && path.indexOf(92) != -1 ? path.replace('\\', '/') : path;
}

public static String separatorsToWindows(String path) {
return path != null && path.indexOf(47) != -1 ? path.replace('/', '\\') : path;
}

public static String transformSeparators(String path, String separators) {
if (path == null) {
return null;
} else {
return separators.indexOf(92) != -1 ? separatorsToWindows(path) : separatorsToUnix(path);
}
}
/* public String convertSeparators(String path,String separator){
PathUtils
String s3=s2.indexOf(90) != -1?s2.replace(, '/'):;
if(s2.indexOf(47) != -1 ){
s3= s2.replace('\\', '/');
}
// 不是 Windows //
if(path.indexOf(47) != -1 ){
return path.replace('\\', '/') : path;
}
String tempPath=path;
String tempSeparator=separator;
if(path.indexOf(47) != -1 ){
return path.replace('\\', '/') ;
}else {
}
}*/


public void removeFile(String channelId, String path) {
getFileManager(channelId).removeFile(path);
}
Expand All @@ -162,7 +231,7 @@ public void rename(String channelId, String oldPath, String fileName) {
getFileManager(channelId).rename(oldPath, fileName);
}

public void download(String channelId, String path) {
public void download(String channelId, String path,boolean isDirectory) {

// todo 当前处理Windows系统
String fileName = FileUtil.getName(path);
Expand All @@ -171,8 +240,22 @@ public void download(String channelId, String path) {
TtyConnector ttyConnector = getTyConnector(channelId);
final String sessionId = ttyConnector.getSessionId();
FileManager fileManager = getFileManager(channelId);
long size = fileManager.getSize(path);
doDownload(TransferInfo.buildTransferInfo(savePath, path, sessionId, size, FileOperate.GET), channelId);
if(isDirectory){
fileManager.fileInfos(path).forEach(fileInfo -> {
String relativePath = transformSeparators(StrUtil.subAfter(fileInfo.path(),path, true), File.separator);
String completePath = Path.of(savePath, relativePath).toString();
if (fileInfo.type() == FileType.DIRECTORY) {
FileUtil.mkdir(completePath);
} else {
long size = fileManager.getSize(fileInfo.path());
doDownload(TransferInfo.buildTransferInfo(completePath, fileInfo.path(), sessionId, size, FileOperate.GET), channelId);
}
});
}else {
long size = fileManager.getSize(path);
doDownload(TransferInfo.buildTransferInfo(savePath, path, sessionId, size, FileOperate.GET), channelId);
}

}

public void deleteRecord(String transferRecordId) {
Expand Down Expand Up @@ -241,7 +324,7 @@ private void doDownload(TransferInfo transferInfo, String channelId, Runnable ca
transferRecord.setStatus(TransferRecord.Status.COMPLETE);
updateTransferRecord(transferRecord);

WebSocket.sendMsg(channelId, WebSocket.MsgType.TRANSFER_COMPLETE, objectMapper.writeValueAsString(new Progress(100.0, channelId, file.getName(), TransferRecord.Status.COMPLETE, transferRecord.id,FileOperate.GET,transferRecord.getReadPath(), transferRecord.getWritePath())));
WebSocket.sendMsg(channelId, WebSocket.MsgType.TRANSFER_COMPLETE, objectMapper.writeValueAsString(new Progress(100.0, channelId, file.getName(), TransferRecord.Status.COMPLETE, transferRecord.id, FileOperate.GET, transferRecord.getReadPath(), transferRecord.getWritePath())));

} catch (IOException e) {
log.error("doUpload channelId{} transferRecord:{} fileName:{} offset:{} error:{}", channelId, transferRecord.id, file.getName(), current[0], e);
Expand All @@ -267,6 +350,7 @@ private void doUpload(TransferInfo transferInfo, String channelId) {
// todo 为以后 多线程做兼容
Breakpoint breakpoint = breakpoints.get(0);
breakpointCache.putIfAbsent(breakpoint.getId(), breakpoint);

taskExecutor.submitUpload(sessionId, () -> getFileManager(channelId).upload(writePath, (outputStream) -> {
File file = FileUtil.file(readPath);

Expand Down Expand Up @@ -294,7 +378,7 @@ private void doUpload(TransferInfo transferInfo, String channelId) {
updateTransferRecord(transferRecord);


WebSocket.sendMsg(channelId, WebSocket.MsgType.TRANSFER_COMPLETE, objectMapper.writeValueAsString(new Progress(100.0, channelId, file.getName(), TransferRecord.Status.COMPLETE, transferRecord.id,FileOperate.PUT, transferRecord.getReadPath(), transferRecord.getWritePath())));
WebSocket.sendMsg(channelId, WebSocket.MsgType.TRANSFER_COMPLETE, objectMapper.writeValueAsString(new Progress(100.0, channelId, file.getName(), TransferRecord.Status.COMPLETE, transferRecord.id, FileOperate.PUT, transferRecord.getReadPath(), transferRecord.getWritePath())));
} catch (IOException e) {
log.error("doUpload channelId{} transferRecord:{} fileName:{} offset:{} error:{}", channelId, transferRecord.id, transferRecord.getWritePath(), current[0], e);
} finally {
Expand All @@ -316,6 +400,7 @@ private void updateTransferRecord(TransferRecord transferRecord) {
}



private void updateBreakpoint(String breakpointId) {
try {
userTransaction.begin();
Expand Down Expand Up @@ -371,7 +456,13 @@ public void continueTransfer(String channelId, String transferRecordId) {


public List<FileInfo> fileInfos(String channelId, String path) {
List<FileInfo> fileInfos = getFileManager(channelId).fileInfos(path);
FileManager fileManager = getFileManager(channelId);
String separator = fileManager.getSeparator();
if (path == null || "null".equals(path)) {
path = separator;
}
String completePath = path.endsWith(separator) ? path : path + separator;
List<FileInfo> fileInfos = fileManager.fileInfos(completePath);
return fileInfos.stream().sorted(Comparator.comparing(FileInfo::type)).toList();
}

Expand Down Expand Up @@ -400,7 +491,7 @@ public List<Progress> getDownloadList(String channelId) {


public record Progress(double percent, String channelId, String fileName, TransferRecord.Status status,
String transferRecordId, FileOperate operate,String readPath,String writePath) {
String transferRecordId, FileOperate operate, String readPath, String writePath) {
public Progress(double percent, String channelId, String fileName, TransferRecord.Status status, String transferRecordId, FileOperate operate, String readPath, String writePath) {
this.percent = percent;
this.channelId = channelId;
Expand Down
15 changes: 8 additions & 7 deletions server/src/main/java/com/tshell/core/JschPtyConnector.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,16 @@ private ChannelSftp getSftpChannel() {

@Override
public void create(String dir, String name, FileType type) {
// todo 优化传参 直接传一个完整路径
String separator = clientHandler.getSeparator();
if (dir == null || "null".equals(dir)) {
dir = separator;
}
String completePath = dir.endsWith(separator) ? dir : dir + separator + name;
String newName = name;
if(!newName.startsWith(separator)){
newName = separator + name;
}
String completePath = dir.endsWith(separator) ? dir : dir + newName;

clientHandler.createFile((cmd) -> {
return cn.hutool.extra.ssh.JschUtil.exec(JschSessionPoll.INSTANCE.getSession(sessionId), cmd, StandardCharsets.UTF_8);
Expand Down Expand Up @@ -313,18 +318,13 @@ public long getSize(String path) {

@Override
public List<FileInfo> fileInfos(String path) {
String separator = clientHandler.getSeparator();
if (path == null || "null".equals(path)) {
path = separator;
}
String completePath = path.endsWith(separator) ? path : path + separator;
return clientHandler.getFileInfos((cmd) -> {
try {
return cn.hutool.extra.ssh.JschUtil.exec(shellChannel.getSession(), cmd, StandardCharsets.UTF_8);
} catch (JSchException e) {
throw new RuntimeException(e);
}
}, completePath);
}, path);
}

/**
Expand All @@ -347,6 +347,7 @@ public FileInfo fileInfo(String path) {
public String getSeparator() {
return clientHandler.getSeparator();
}

}


Expand Down
4 changes: 2 additions & 2 deletions ui/src/theblind_shell/service/shell/fileManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ export function removeDirectory(channelId: any, path: string) {
{ headers: { 'Content-Type': EnumContentType.formUrlencoded } }
);
}
export function download(channelId: any, path: any) {
export function download(channelId: any, path: any,isDirectory:any) {
return request.post(
`/fileManager/download/${channelId}`,
{ path },
{ path,isDirectory },
{ headers: { 'Content-Type': EnumContentType.formUrlencoded } }
);
}
Expand Down
13 changes: 10 additions & 3 deletions ui/src/views/shell/terminal/components/FileManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ const fileOptions = [
},
{
label: '下载',
key: 'download'
key: 'downloadFile'
},
{
label: () => h('span', { style: { color: 'red' } }, '删除'),
Expand All @@ -315,6 +315,10 @@ const directoryOptions = [
label: '重命名',
key: 'rename'
},
{
label: '下载',
key: 'downloadDirectory'
},
{
label: () => h('span', { style: { color: 'red' } }, '删除'),
key: 'deleteDirectory'
Expand Down Expand Up @@ -752,9 +756,12 @@ const handleSelect = key => {
formValue.value.fileName = null;
modalOperate = key;
break;
case 'download':
download(props.channelId, currentSelectPath);
case 'downloadFile':
download(props.channelId, currentSelectPath,false);
break;
case 'downloadDirectory':
download(props.channelId, currentSelectPath,true);
break;
case 'deleteFile':
removeFile(props.channelId, currentSelectPath).then(() => {
loadFileInfos(currentDirectory.value);
Expand Down

0 comments on commit 6164482

Please # to comment.