Compare commits
6 Commits
05bc8381cd
...
master
Author | SHA256 | Date | |
---|---|---|---|
82b9c12773
|
|||
18571e0e0e
|
|||
0a7de4fb9b
|
|||
21f3d7e6ce
|
|||
fb3f79e468
|
|||
bb6346f1da
|
44
pom.xml
44
pom.xml
@ -4,22 +4,56 @@
|
|||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.5.0</version>
|
||||||
|
<relativePath/>
|
||||||
|
</parent>
|
||||||
|
|
||||||
<groupId>ua.com.dxrkness</groupId>
|
<groupId>ua.com.dxrkness</groupId>
|
||||||
<artifactId>aws-example</artifactId>
|
<artifactId>cloud-tech</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>0.1</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>23</maven.compiler.source>
|
<maven.compiler.release>21</maven.compiler.release>
|
||||||
<maven.compiler.target>23</maven.compiler.target>
|
<java.version>21</java.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|
||||||
|
<s3.version>2.31.47</s3.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>software.amazon.awssdk</groupId>
|
<groupId>software.amazon.awssdk</groupId>
|
||||||
<artifactId>s3</artifactId>
|
<artifactId>s3</artifactId>
|
||||||
<version>2.25.27</version>
|
<version>${s3.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-jetty</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
</project>
|
</project>
|
@ -1,18 +1,11 @@
|
|||||||
package ua.com.dxrkness;
|
package ua.com.dxrkness;
|
||||||
|
|
||||||
import software.amazon.awssdk.regions.Region;
|
import org.springframework.boot.SpringApplication;
|
||||||
import software.amazon.awssdk.services.s3.S3Client;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
public class Main {
|
public class Main {
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Main.class, args);
|
||||||
final var s3 = S3Client.builder()
|
|
||||||
.region(Region.EU_CENTRAL_1)
|
|
||||||
.build();
|
|
||||||
try (s3) {
|
|
||||||
s3.listBuckets().buckets().forEach(System.out::println);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,41 +0,0 @@
|
|||||||
package ua.com.dxrkness;
|
|
||||||
|
|
||||||
import software.amazon.awssdk.services.s3.S3Client;
|
|
||||||
import software.amazon.awssdk.services.s3.model.S3Object;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class S3Service {
|
|
||||||
private final S3Client client;
|
|
||||||
private final String bucketName;
|
|
||||||
|
|
||||||
public S3Service(S3Client client, String bucketName) {
|
|
||||||
this.client = client;
|
|
||||||
this.bucketName = bucketName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void createDirectory(Path path) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Boolean delete(Path path) {
|
|
||||||
return client
|
|
||||||
.deleteObject(b -> b.bucket(bucketName).key(path.toString()).build())
|
|
||||||
.deleteMarker();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<S3Object> listing(Path path) {
|
|
||||||
return client
|
|
||||||
.listObjectsV2(b -> b.bucket(bucketName).build())
|
|
||||||
.contents();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void uploadFile(Path path) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String downloadFile(Path path) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
16
src/main/java/ua/com/dxrkness/config/S3Configuration.java
Normal file
16
src/main/java/ua/com/dxrkness/config/S3Configuration.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package ua.com.dxrkness.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import software.amazon.awssdk.regions.Region;
|
||||||
|
import software.amazon.awssdk.services.s3.S3Client;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class S3Configuration {
|
||||||
|
@Bean
|
||||||
|
public S3Client client() {
|
||||||
|
return S3Client.builder()
|
||||||
|
.region(Region.EU_CENTRAL_1)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
101
src/main/java/ua/com/dxrkness/controller/S3FileHandler.java
Normal file
101
src/main/java/ua/com/dxrkness/controller/S3FileHandler.java
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package ua.com.dxrkness.controller;
|
||||||
|
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.servlet.function.ServerRequest;
|
||||||
|
import org.springframework.web.servlet.function.ServerResponse;
|
||||||
|
import software.amazon.awssdk.services.s3.model.S3Exception;
|
||||||
|
import ua.com.dxrkness.model.DirectoryPath;
|
||||||
|
import ua.com.dxrkness.service.S3Service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class S3FileHandler {
|
||||||
|
private final S3Service service;
|
||||||
|
|
||||||
|
public S3FileHandler(S3Service service) {
|
||||||
|
this.service = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerResponse createDirectory(ServerRequest request) throws ServletException, IOException {
|
||||||
|
return Optional.of(request.body(DirectoryPath.class))
|
||||||
|
.map(DirectoryPath::path)
|
||||||
|
.map(body -> {
|
||||||
|
try {
|
||||||
|
service.createDirectory(body);
|
||||||
|
return ServerResponse.created(body).build();
|
||||||
|
} catch (S3Exception exception) {
|
||||||
|
return ServerResponse.badRequest().build();
|
||||||
|
}
|
||||||
|
}).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerResponse downloadFile(ServerRequest request) {
|
||||||
|
return Optional.of(request.pathVariable("path").substring(1))
|
||||||
|
.map(URI::create)
|
||||||
|
.map(service::downloadFile)
|
||||||
|
.map(reader -> {
|
||||||
|
try (reader) {
|
||||||
|
return ServerResponse
|
||||||
|
.ok()
|
||||||
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
.body(reader.readAllBytes());
|
||||||
|
} catch (IOException e) {
|
||||||
|
return ServerResponse.badRequest().build();
|
||||||
|
}
|
||||||
|
}).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerResponse uploadFile(ServerRequest request) throws ServletException, IOException {
|
||||||
|
return Optional.of(request.multipartData())
|
||||||
|
.map(data -> data.getFirst("file"))
|
||||||
|
.map(file -> {
|
||||||
|
try {
|
||||||
|
final var path = request.param("path")
|
||||||
|
.map(URI::create)
|
||||||
|
.orElse(URI.create(""));
|
||||||
|
|
||||||
|
service.uploadFile(file, path);
|
||||||
|
return ServerResponse
|
||||||
|
.created(URI.create("/files"))
|
||||||
|
.body(new DirectoryPath(
|
||||||
|
path.resolve(
|
||||||
|
file.getSubmittedFileName().replaceAll("\s", "_")
|
||||||
|
)
|
||||||
|
));
|
||||||
|
} catch (IOException e) {
|
||||||
|
return ServerResponse.badRequest().build();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerResponse listing(ServerRequest request) {
|
||||||
|
return Optional.of(request.pathVariable("path").substring(1))
|
||||||
|
.map(key -> service.listing(URI.create(key)))
|
||||||
|
.map(objects -> ServerResponse.ok().body(objects))
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerResponse delete(ServerRequest request) throws ServletException, IOException {
|
||||||
|
return Optional.of(request.body(DirectoryPath.class))
|
||||||
|
.map(DirectoryPath::path)
|
||||||
|
.map(path -> {
|
||||||
|
try {
|
||||||
|
service.delete(path);
|
||||||
|
return ServerResponse.noContent().build();
|
||||||
|
} catch (S3Exception e) {
|
||||||
|
return ServerResponse.badRequest().build();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerResponse listBuckets(ServerRequest request) {
|
||||||
|
return ServerResponse.ok().body(service.listBuckets());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package ua.com.dxrkness.controller;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
import org.springframework.web.servlet.function.RouterFunction;
|
||||||
|
import org.springframework.web.servlet.function.RouterFunctions;
|
||||||
|
import org.springframework.web.servlet.function.ServerResponse;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class S3FileHandlerConfig implements WebMvcConfigurer {
|
||||||
|
@Bean
|
||||||
|
public RouterFunction<ServerResponse> router(S3FileHandler handler) {
|
||||||
|
return RouterFunctions.route()
|
||||||
|
.path("/files", b -> b
|
||||||
|
.GET("/{*path}", handler::downloadFile)
|
||||||
|
.POST(handler::uploadFile)
|
||||||
|
)
|
||||||
|
.path("/dirs", b -> b
|
||||||
|
.GET("/{*path}", handler::listing)
|
||||||
|
.POST(handler::createDirectory)
|
||||||
|
)
|
||||||
|
.path("/common", b -> b
|
||||||
|
.GET(handler::listBuckets)
|
||||||
|
.DELETE(handler::delete)
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
|
registry.addMapping("/**")
|
||||||
|
.allowedMethods("*")
|
||||||
|
.allowedHeaders("*")
|
||||||
|
.allowedOriginPatterns("*");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
|
registry.addResourceHandler("/public/**")
|
||||||
|
.addResourceLocations("classpath:/static/");
|
||||||
|
}
|
||||||
|
}
|
13
src/main/java/ua/com/dxrkness/function/Action.java
Normal file
13
src/main/java/ua/com/dxrkness/function/Action.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package ua.com.dxrkness.function;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Action {
|
||||||
|
void execute();
|
||||||
|
|
||||||
|
default Action andThen(Action after) {
|
||||||
|
return () -> {
|
||||||
|
this.execute();
|
||||||
|
after.execute();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
11
src/main/java/ua/com/dxrkness/model/DirectoryPath.java
Normal file
11
src/main/java/ua/com/dxrkness/model/DirectoryPath.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package ua.com.dxrkness.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import ua.com.dxrkness.model.serialization.URIDeserializer;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
public record DirectoryPath(
|
||||||
|
@JsonDeserialize(using = URIDeserializer.class)
|
||||||
|
URI path
|
||||||
|
) { }
|
@ -0,0 +1,16 @@
|
|||||||
|
package ua.com.dxrkness.model.serialization;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JacksonException;
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
public class URIDeserializer extends JsonDeserializer<URI> {
|
||||||
|
@Override
|
||||||
|
public URI deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
|
||||||
|
return URI.create(jsonParser.getText());
|
||||||
|
}
|
||||||
|
}
|
125
src/main/java/ua/com/dxrkness/service/S3Service.java
Normal file
125
src/main/java/ua/com/dxrkness/service/S3Service.java
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package ua.com.dxrkness.service;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.Part;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import software.amazon.awssdk.core.sync.RequestBody;
|
||||||
|
import software.amazon.awssdk.services.s3.S3Client;
|
||||||
|
import software.amazon.awssdk.services.s3.model.Bucket;
|
||||||
|
import software.amazon.awssdk.services.s3.model.ObjectIdentifier;
|
||||||
|
import software.amazon.awssdk.services.s3.model.S3Object;
|
||||||
|
import ua.com.dxrkness.function.Action;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class S3Service {
|
||||||
|
private final S3Client client;
|
||||||
|
private final String bucketName;
|
||||||
|
|
||||||
|
public S3Service(S3Client client,
|
||||||
|
@Value("${s3.bucket-name}") String bucketName) {
|
||||||
|
this.client = client;
|
||||||
|
this.bucketName = bucketName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createDirectory(URI path) {
|
||||||
|
final var pathString = Optional.of(path)
|
||||||
|
.map(URI::toString)
|
||||||
|
.filter(p -> p.charAt(p.length() - 1) == '/')
|
||||||
|
.orElse(path.toString() + '/');
|
||||||
|
|
||||||
|
Action createDir = () -> client.putObject(b -> b
|
||||||
|
.bucket(bucketName)
|
||||||
|
.key(pathString)
|
||||||
|
.build(),
|
||||||
|
RequestBody.empty());
|
||||||
|
Action waitForCreation = () -> client.waiter()
|
||||||
|
.waitUntilObjectExists(b -> b
|
||||||
|
.bucket(bucketName)
|
||||||
|
.key(pathString)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
createDir.andThen(waitForCreation).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(URI path) {
|
||||||
|
Function<Void, List<ObjectIdentifier>> listAllObjectInDirectory = (ignored) ->
|
||||||
|
client.listObjectsV2(b -> b
|
||||||
|
.prefix(path.toString())
|
||||||
|
.bucket(bucketName)
|
||||||
|
.build())
|
||||||
|
.contents().stream()
|
||||||
|
.map(S3Object::key)
|
||||||
|
.map(key -> ObjectIdentifier.builder()
|
||||||
|
.key(key)
|
||||||
|
.build())
|
||||||
|
.toList();
|
||||||
|
Consumer<List<ObjectIdentifier>> deleteObjects =
|
||||||
|
keys -> client.deleteObjects(b -> b
|
||||||
|
.bucket(bucketName)
|
||||||
|
.delete(b1 -> b1
|
||||||
|
.objects(keys)));
|
||||||
|
Action waitForDeletion = () -> client.waiter()
|
||||||
|
.waitUntilObjectNotExists(b -> b
|
||||||
|
.bucket(bucketName)
|
||||||
|
.key(path.toString())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
listAllObjectInDirectory
|
||||||
|
.andThen(keys -> {
|
||||||
|
deleteObjects.accept(keys);
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.andThen(n -> {
|
||||||
|
waitForDeletion.execute();
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.apply(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> listing(URI path) {
|
||||||
|
return client
|
||||||
|
.listObjectsV2(b -> b
|
||||||
|
.bucket(bucketName)
|
||||||
|
.prefix(path.toString())
|
||||||
|
.build())
|
||||||
|
.contents()
|
||||||
|
.stream()
|
||||||
|
.map(S3Object::key)
|
||||||
|
.filter(filePath -> !filePath.equals(path.toString()))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void uploadFile(Part file, URI destinationPath) throws IOException {
|
||||||
|
client.putObject(b -> b
|
||||||
|
.bucket(bucketName)
|
||||||
|
.key(destinationPath
|
||||||
|
.resolve(file.getSubmittedFileName().replace("\s", "_"))
|
||||||
|
.toString()),
|
||||||
|
RequestBody.fromInputStream(file.getInputStream(), file.getSize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferedInputStream downloadFile(URI key) {
|
||||||
|
return new BufferedInputStream(
|
||||||
|
Channels.newInputStream(
|
||||||
|
Channels.newChannel(client.getObject(b -> b
|
||||||
|
.bucket(bucketName)
|
||||||
|
.key(key.toString())))));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> listBuckets() {
|
||||||
|
return client.listBuckets()
|
||||||
|
.buckets()
|
||||||
|
.stream()
|
||||||
|
.map(Bucket::name)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
1
src/main/resources/application.properties
Normal file
1
src/main/resources/application.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
s3.bucket-name=student-test-bucket-orlovos-2025
|
261
src/main/resources/static/common.html
Normal file
261
src/main/resources/static/common.html
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Unified</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Unified</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2>Current Directory: <span id="currentPath">/</span></h2>
|
||||||
|
<button onclick="goToParent()">Parent Directory</button>
|
||||||
|
<button onclick="refreshDirectory()">Refresh</button>
|
||||||
|
<input type="text" id="newDirName" placeholder="New directory name">
|
||||||
|
<button onclick="createDirectoryHere()">Create Directory</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>Directory Contents</h3>
|
||||||
|
<div id="directoryContents">Loading...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>File Operations</h3>
|
||||||
|
<input type="file" id="fileUpload">
|
||||||
|
<button onclick="uploadFileHere()">Upload to Current Directory</button>
|
||||||
|
<div id="fileOperationResult"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>Navigation</h3>
|
||||||
|
<input type="text" id="pathInput" placeholder="Enter path to navigate to">
|
||||||
|
<button onclick="navigateToPath()">Go to Path</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>Just a meme from S3 bucket</h3>
|
||||||
|
<img width="40%"
|
||||||
|
src="https://student-test-bucket-orlovos-2025.s3.eu-central-1.amazonaws.com/meme.jpg">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const baseUrl = 'http://18.199.97.136:8080';
|
||||||
|
let currentPath = '';
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
refreshDirectory();
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateCurrentPathDisplay() {
|
||||||
|
document.getElementById('currentPath').textContent = currentPath;
|
||||||
|
document.getElementById('pathInput').value = currentPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshDirectory() {
|
||||||
|
const contentsDiv = document.getElementById('directoryContents');
|
||||||
|
contentsDiv.innerHTML = 'Loading...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${baseUrl}/dirs/${decodeURIComponent(currentPath)}`, {
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
displayDirectoryContents(data);
|
||||||
|
} else {
|
||||||
|
const errorText = await response.text();
|
||||||
|
contentsDiv.innerHTML = `<p style="color: red;">Error loading directory: ${response.status} - ${errorText}</p>`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
contentsDiv.innerHTML = `<p style="color: red;">Network error: ${error.message}</p>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayDirectoryContents(items) {
|
||||||
|
const contentsDiv = document.getElementById('directoryContents');
|
||||||
|
|
||||||
|
if (!Array.isArray(items) || items.length === 0) {
|
||||||
|
contentsDiv.innerHTML = '<p>Directory is empty</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let html = '<table border="1">';
|
||||||
|
html += '<tr><th>Name</th><th>Type</th><th>Actions</th></tr>';
|
||||||
|
|
||||||
|
items.forEach(item => {
|
||||||
|
const isDirectory = item.endsWith('/');
|
||||||
|
const name = item;
|
||||||
|
const fullPath = item;
|
||||||
|
|
||||||
|
html += '<tr>';
|
||||||
|
html += `<td>${name}</td>`;
|
||||||
|
html += `<td>${isDirectory ? 'Directory' : 'File'}</td>`;
|
||||||
|
html += '<td>';
|
||||||
|
|
||||||
|
if (isDirectory) {
|
||||||
|
html += `<button onclick="navigateToDirectory('${fullPath}')">Open</button> `;
|
||||||
|
} else {
|
||||||
|
html += `<button onclick="downloadFileFromPath('${fullPath}')">Download</button> `;
|
||||||
|
}
|
||||||
|
|
||||||
|
html += `<button onclick="removeItem('${fullPath}')" style="color: red;">Delete</button>`;
|
||||||
|
html += '</td>';
|
||||||
|
html += '</tr>';
|
||||||
|
});
|
||||||
|
|
||||||
|
html += '</table>';
|
||||||
|
contentsDiv.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function navigateToDirectory(path) {
|
||||||
|
currentPath = path;
|
||||||
|
updateCurrentPathDisplay();
|
||||||
|
refreshDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
function navigateToPath() {
|
||||||
|
const path = document.getElementById('pathInput').value;
|
||||||
|
navigateToDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToParent() {
|
||||||
|
if (currentPath !== '') {
|
||||||
|
const parts = currentPath.split('/').filter(p => p);
|
||||||
|
parts.pop();
|
||||||
|
currentPath = parts.length > 0 ? '' + parts.join('/') + '/' : '';
|
||||||
|
updateCurrentPathDisplay();
|
||||||
|
refreshDirectory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createDirectoryHere() {
|
||||||
|
const dirName = document.getElementById('newDirName').value;
|
||||||
|
const resultDiv = document.getElementById('fileOperationResult');
|
||||||
|
|
||||||
|
if (!dirName) {
|
||||||
|
resultDiv.innerHTML = '<p style="color: red;">Please enter a directory name</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newDirPath = currentPath === '' ? `${dirName}/` : `${currentPath}${dirName}/`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${baseUrl}/dirs`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ path: newDirPath })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
resultDiv.innerHTML = `<p style="color: green;">Directory created: ${dirName}</p>`;
|
||||||
|
document.getElementById('newDirName').value = '';
|
||||||
|
await refreshDirectory();
|
||||||
|
} else {
|
||||||
|
const errorText = await response.text();
|
||||||
|
resultDiv.innerHTML = `<p style="color: red;">Error: ${response.status} - ${errorText}</p>`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
resultDiv.innerHTML = `<p style="color: red;">Network error: ${error.message}</p>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function uploadFileHere() {
|
||||||
|
const fileInput = document.getElementById('fileUpload');
|
||||||
|
const resultDiv = document.getElementById('fileOperationResult');
|
||||||
|
|
||||||
|
if (!fileInput.files[0]) {
|
||||||
|
resultDiv.innerHTML = '<p style="color: red;">Please select a file</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = fileInput.files[0];
|
||||||
|
const filePath = currentPath === '/' ? `/${file.name}` : `${currentPath}/${file.name}`;
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
formData.append('dirPath', JSON.stringify({ path: filePath }));
|
||||||
|
|
||||||
|
try {
|
||||||
|
resultDiv.innerHTML = '<p>Uploading...</p>';
|
||||||
|
|
||||||
|
const response = await fetch(`${baseUrl}/files`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
resultDiv.innerHTML = `<p style="color: green;">File uploaded: ${file.name}</p>`;
|
||||||
|
fileInput.value = '';
|
||||||
|
await refreshDirectory();
|
||||||
|
} else {
|
||||||
|
const errorText = await response.text();
|
||||||
|
resultDiv.innerHTML = `<p style="color: red;">Error: ${response.status} - ${errorText}</p>`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
resultDiv.innerHTML = `<p style="color: red;">Network error: ${error.message}</p>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadFileFromPath(filePath) {
|
||||||
|
const resultDiv = document.getElementById('fileOperationResult');
|
||||||
|
|
||||||
|
try {
|
||||||
|
resultDiv.innerHTML = '<p>Downloading...</p>';
|
||||||
|
|
||||||
|
const response = await fetch(`${baseUrl}/files/${decodeURIComponent(filePath)}`, {
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const blob = await response.blob();
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = filePath.split('/').pop() || 'download';
|
||||||
|
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
document.body.removeChild(a);
|
||||||
|
|
||||||
|
resultDiv.innerHTML = `<p style="color: green;">File downloaded successfully</p>`;
|
||||||
|
} else {
|
||||||
|
const errorText = await response.text();
|
||||||
|
resultDiv.innerHTML = `<p style="color: red;">Error: ${response.status} - ${errorText}</p>`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
resultDiv.innerHTML = `<p style="color: red;">Network error: ${error.message}</p>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeItem(path) {
|
||||||
|
const resultDiv = document.getElementById('fileOperationResult');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${baseUrl}/common`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ path: path })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
resultDiv.innerHTML = `<p style="color: green;">Successfully removed: ${path}</p>`;
|
||||||
|
await refreshDirectory();
|
||||||
|
} else {
|
||||||
|
const errorText = await response.text();
|
||||||
|
resultDiv.innerHTML = `<p style="color: red;">Error: ${response.status} - ${errorText}</p>`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
resultDiv.innerHTML = `<p style="color: red;">Network error: ${error.message}</p>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Reference in New Issue
Block a user