Skip to content

Instantly share code, notes, and snippets.

@winder
Created January 3, 2020 15:10
Show Gist options
  • Save winder/73a46de22ccb89a34ab85b46830848e2 to your computer and use it in GitHub Desktop.
Save winder/73a46de22ccb89a34ab85b46830848e2 to your computer and use it in GitHub Desktop.
AWS S3 JFileChooser
public final class Example {
final static String access = "access-id-here";
final static String secret = "secret-key-here";
public static void main(String[] args) throws Exception {
S3FileSystemView viewer = new S3FileSystemView(access, secret);
JFileChooser chooser = new JFileChooser("s3:/", viewer);
int returnVal = chooser.showOpenDialog(new JFrame());
if (returnVal == JFileChooser.APPROVE_OPTION) {
File f = chooser.getSelectedFile();
System.out.println("Found a file! It is " + f);
} else {
System.out.println("No file selected...");
}
}
}
/*
MIT License
Copyright (c) 2020 Will Winder
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import io.minio.MinioClient;
import io.minio.Result;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.Icon;
import javax.swing.filechooser.FileSystemView;
import org.openide.util.Exceptions;
/**
*
* @author Will Winder
*/
public class S3FileSystemView extends FileSystemView {
// Must match "s3://<bucket>" or "s3://<bucket>/"
// Must match "s3:/<bucket>" or "s3:/<bucket>/"
final static Pattern parseURI = Pattern.compile("^s3://?(?<bucket>[^/]+)/?(?<path>.*)$");
private static boolean hasBucket(File f) {
return parseURI(f).matches();
}
private static Matcher parseURI(File f) {
String path = f.toString();
return parseURI.matcher(path);
}
final String id;
final String secret;
final MinioClient minioClient;
public S3FileSystemView(final String id, final String secret) {
this.id = id;
this.secret = secret;
try {
minioClient = new MinioClient("https://s3.amazonaws.com", id, secret);
} catch (Exception ex) {
throw new RuntimeException("Unable to create S3 Client.");
}
}
@Override
protected File createFileSystemRoot(File f) {
return new VirtualFile(f, 1);
}
@Override
public boolean isComputerNode(File dir) {
return false;
}
@Override
public boolean isFloppyDrive(File dir) {
return false;
}
@Override
public boolean isDrive(File dir) {
return false;
}
@Override
public Icon getSystemIcon(File f) {
return null;
}
@Override
public String getSystemTypeDescription(File f) {
return f.toPath().toString();
}
@Override
public String getSystemDisplayName(File f) {
return f.getName();
}
@Override
public File getParentDirectory(final File dir) {
return dir.getParentFile();
}
@Override
public File[] getFiles(final File dir, boolean useFileHiding) {
// If there is no bucket (i.e. just "s3:/"), add the buckets.
if (!hasBucket(dir)) {
try {
final List<File> files = new ArrayList<>(1);
List<Bucket> bucketList = minioClient.listBuckets();
for (Bucket bucket : bucketList) {
System.out.println(bucket.creationDate() + ", " + bucket.name());
files.add(new VirtualFile("s3:/" + bucket.name() + "/", 0));
}
return files.toArray(new File[files.size()]);
} catch (Exception ex) {
ex.printStackTrace();
}
return new VirtualFile[0];
}
ArrayList<VirtualFile> ret = new ArrayList<>();
Matcher m = parseURI(dir);
try {
if (m.matches()) {
String bucket = m.group("bucket");
// Path should start with a slash unless it's empty
String path = m.group("path");
if (! "".equals(path)) {
path += "/";
}
Iterable<Result<Item>> objects = minioClient.listObjects(bucket, path, false);
String prefix = "s3:/" + bucket + "/";
String dirMatch = dir.toString() + "/";
for (Result<Item> res : objects) {
Item i = res.get();
String name = prefix + i.objectName();
// listObjects matches the current directory, filter it out.
if (name.equals(dirMatch)) {
continue;
}
VirtualFile f = new VirtualFile(name, i.objectSize());
if (!f.isDir) {
f.setLastModified(i.lastModified().getTime());
}
ret.add(f);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return ret.toArray(new VirtualFile[0]);
}
@Override
public File createFileObject(final String path) {
return new VirtualFile(path, 1);
}
@Override
public File createFileObject(final File dir, final String filename) {
throw new UnsupportedOperationException("Sorry, no support for creating files in S3.");
}
@Override
public File getDefaultDirectory() {
return new VirtualFile("s3:/", 1);
}
@Override
public File getHomeDirectory() {
return getDefaultDirectory();
}
@Override
public File[] getRoots() {
final List<File> files = new ArrayList<>(1);
files.add(new VirtualFile("s3:/", 1));
return files.toArray(new VirtualFile[0]);
}
@Override
public boolean isFileSystemRoot(final File dir) {
return !hasBucket(dir);
}
@Override
public boolean isHiddenFile(final File f) {
return false;
}
@Override
public boolean isFileSystem(final File f) {
return !isFileSystemRoot(f);
}
@Override
public File getChild(final File parent, final String fileName) {
throw new UnsupportedOperationException("Not sure when this would make sense. Call getFiles instead.");
}
@Override
public boolean isParent(final File folder, final File file) {
return file.toPath().getParent().equals(folder.toPath());
}
@Override
public Boolean isTraversable(final File f) {
return f.isDirectory();
}
@Override
public boolean isRoot(final File f) {
// Root should just be "s3:/" or "s3:", so check that there is no bucket.
boolean hasBucket = hasBucket(f);
return hasBucket == false;
}
@Override
public File createNewFolder(final File containingDir) throws IOException {
throw new UnsupportedOperationException("Sorry, no support for editing S3.");
}
private class VirtualFile extends File {
private static final long serialVersionUID = -1752685357864733168L;
private final boolean isDir;
private final long length;
private long lastModified = 0;
private VirtualFile(final File file, long length) {
this(file.toString(), length);
}
private VirtualFile(String pathname, long length) {
super(pathname);
isDir = pathname.endsWith("/");
this.length = length;
}
private VirtualFile(String parent, String child, long length) {
super(parent, child);
isDir = child.endsWith("/");
this.length = length;
}
private VirtualFile(File parent, String child, long length) {
super(parent, child);
isDir = child.endsWith("/");
this.length = length;
}
@Override
public boolean setLastModified(long t) {
this.lastModified = t;
return true;
}
@Override
public long lastModified() {
return lastModified;
}
@Override
public long length() {
return length;
}
@Override
public boolean exists() {
return true;
}
@Override
public boolean isDirectory() {
return isDir;
}
@Override
public File getCanonicalFile() throws IOException {
return this;
}
@Override
public File getAbsoluteFile() {
return this;
}
@Override
public File getParentFile() {
final int lastIndex = this.toString().lastIndexOf('/');
if (lastIndex == -1) return null;
String parent = this.toString().substring(0, lastIndex + 1);
return new VirtualFile(parent, 1);
}
}
}
@winder
Copy link
Author

winder commented Jan 3, 2020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment