Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support Rename and Edit Staitc Files (#573) #819

Merged
merged 10 commits into from
May 11, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import run.halo.app.service.StaticStorageService;

import java.util.List;
import java.util.Map;

/**
* Static storage controller.
Expand Down Expand Up @@ -49,4 +50,17 @@ public void upload(String basePath,
@RequestPart("file") MultipartFile file) {
staticStorageService.upload(basePath, file);
}

@PostMapping("rename")
@ApiOperation("Renames static file")
public void rename(String basePath,
String newName) {
staticStorageService.rename(basePath, newName);
}

@PutMapping("save")
ruibaby marked this conversation as resolved.
Show resolved Hide resolved
@ApiOperation("Save static file")
public void save(@RequestBody Map<String, String> data) {
Arexh marked this conversation as resolved.
Show resolved Hide resolved
staticStorageService.save(data.get("basePath"), data.get("content"));
}
}
16 changes: 16 additions & 0 deletions src/main/java/run/halo/app/service/StaticStorageService.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,20 @@ public interface StaticStorageService {
* @param file file must not be null.
*/
void upload(String basePath, @NonNull MultipartFile file);

/**
* Rename static file or folder.
*
* @param basePath base path must not be null
* @param newName new name must not be null
*/
void rename(@NonNull String basePath, @NonNull String newName);

/**
* Save static file.
*
* @param basePath base path must not be null
* @param content saved content
*/
void save(@NonNull String basePath, String content);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@

import javax.activation.MimetypesFileTypeMap;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Stream;
Expand Down Expand Up @@ -167,6 +166,42 @@ public void upload(String basePath, MultipartFile file) {
}
}

@Override
public void rename(String basePath, String newName) {
Assert.notNull(basePath, "Base path must not be null");
Assert.notNull(newName, "New name must not be null");

Path pathToRename;

if (StringUtils.startsWith(newName, API_FOLDER_NAME)) {
throw new FileOperationException("重命名名称 " + newName + " 不合法");
}

pathToRename = Paths.get(staticDir.toString(), basePath);
ruibaby marked this conversation as resolved.
Show resolved Hide resolved

try {
FileUtils.rename(pathToRename, newName);
onChange();
} catch (FileAlreadyExistsException e) {
throw new FileOperationException("该路径下名称 " + newName + " 已存在");
} catch (IOException e) {
throw new FileOperationException("重命名 " + pathToRename.toString() + " 失败");
}
}

@Override
public void save(String basePath, String content) {
Assert.notNull(basePath, "Base path must not be null");

Path path = Paths.get(staticDir.toString(), basePath);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里是否存在 Directory traversal 风险呢?


try {
Files.write(path, content.getBytes(StandardCharsets.UTF_8));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

写入前是否需要检查一下该文件是否存在呢?

} catch (IOException e) {
throw new ServiceException("保存内容失败 " + path, e);
}
}

private void onChange() {
eventPublisher.publishEvent(new StaticStorageChangedEvent(this, staticDir));
}
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/run/halo/app/utils/FileUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,24 @@ public static void deleteFolder(@NonNull Path deletingPath) throws IOException {
log.info("Deleted [{}] successfully", deletingPath);
}

/**
* Renames file or folder.
*
* @param pathToRename file path to rename must not be null
* @param newName new name must not be null
*/
public static void rename(@NonNull Path pathToRename, @NonNull String newName) throws IOException {
Assert.notNull(pathToRename, "File path to rename must not be null");
Assert.notNull(newName, "New name must not be null");

Path newPath = pathToRename.resolveSibling(newName);
log.info("Rename [{}] to [{}]", pathToRename.toString(), newPath.toString());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

可以省略 toString() 方法,这样还可能会造成 NPE。


Files.move(pathToRename, newPath);

log.info("Rename [{}] successfully", pathToRename.toString());
}

/**
* Unzips content to the target path.
*
Expand Down
87 changes: 87 additions & 0 deletions src/test/java/run/halo/app/utils/FileUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand Down Expand Up @@ -108,4 +109,90 @@ public void dbFileReadTest() throws IOException {
log.debug("Buffer String: [{}]", bufString);
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

也许应该再增加一个关于 directory traversal 的测试。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Impl的测试单元要怎么写?上次我提交了一个测试,结果导致隔壁的测试挂了,估计是注解写的不对。可以给个ImplTest的样例代码吗?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

早上给你回复。

@Test
public void testRenameFile() throws IOException {
// Create a temp folder
Path tempDirectory = Files.createTempDirectory("halo-test");

Path testPath = tempDirectory.resolve("test/test");
Path filePath = tempDirectory.resolve("test/test/test.file");

// Create a temp file and folder
Files.createDirectories(testPath);
Files.createFile(filePath);

// Write content to the temp file
String content = "Test Content!\n";
Files.write(filePath, content.getBytes());

// Rename temp file
FileUtils.rename(filePath, "newName");
Path newPath = filePath.resolveSibling("newName");

Assert.assertFalse(Files.exists(filePath));
Assert.assertTrue(Files.isRegularFile(newPath));
Assert.assertEquals(new String(Files.readAllBytes(newPath)), content);

FileUtils.deleteFolder(tempDirectory);
}

@Test
public void testRenameFolder() throws IOException {
// Create a temp folder
Path tempDirectory = Files.createTempDirectory("halo-test");

Path testPath = tempDirectory.resolve("test/test");
Path filePath = tempDirectory.resolve("test/test.file");

// Create a temp file and folder
Files.createDirectories(testPath);
Files.createFile(filePath);

// Rename temp folder
FileUtils.rename(tempDirectory.resolve("test"), "newName");
Path newPath = tempDirectory.resolve("newName");

Assert.assertTrue(Files.isDirectory(newPath));
Assert.assertTrue(Files.isRegularFile(newPath.resolve("test.file")));

FileUtils.deleteFolder(tempDirectory);
}

@Test
public void testRenameRepeat() throws IOException {
// Create a temp folder
Path tempDirectory = Files.createTempDirectory("halo-test");

Path testPathOne = tempDirectory.resolve("test/testOne");
Path testPathTwo = tempDirectory.resolve("test/testTwo");
Path filePathOne = tempDirectory.resolve("test/testOne.file");
Path filePathTwo = tempDirectory.resolve("test/testTwo.file");

// Create temp files and folders
Files.createDirectories(testPathOne);
Files.createDirectories(testPathTwo);
Files.createFile(filePathOne);
Files.createFile(filePathTwo);

try {
FileUtils.rename(testPathOne, "testTwo");
} catch (Exception e) {
Assert.assertTrue(e instanceof FileAlreadyExistsException);
}

try {
FileUtils.rename(filePathOne, "testTwo.file");
} catch (Exception e) {
Assert.assertTrue(e instanceof FileAlreadyExistsException);
}

try {
FileUtils.rename(filePathOne, "testOne");
} catch (Exception e) {
Assert.assertTrue(e instanceof FileAlreadyExistsException);
}

FileUtils.deleteFolder(tempDirectory);
}
}