我正在尝试开发一些东西,出于学习目的,允许我将任何类型的网络资源(所以HTTP / FTP目录和类似的东西)安装为本地文件夹。
我在这个库中使用FUSE + JAVA:https://github.com/EtiennePerot/fuse-jna
我之所以选择JAVA是因为我需要将其作为Web服务工作,而我只知道如何使用JAVA。
现在,在这个简短的介绍之后,这是我的情景:
_______________ ____________ __________
| | | | | |
|remote resource|<--HTTP/FTP-->|fuse machine|<--SAMBA-->|desktop pc|
|_______________| |____________| |__________|
我从这个例子开始:
package net.fusejna.examples;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import net.fusejna.DirectoryFiller;
import net.fusejna.ErrorCodes;
import net.fusejna.StructFuseFileInfo.FileInfoWrapper;
import net.fusejna.StructStat.StatWrapper;
import net.fusejna.types.TypeMode.ModeWrapper;
import net.fusejna.types.TypeMode.NodeType;
import net.fusejna.util.FuseFilesystemAdapterAssumeImplemented;
public final class MemoryFS extends FuseFilesystemAdapterAssumeImplemented
{
private final class MemoryDirectory extends MemoryPath
{
private final List<MemoryPath> contents = new ArrayList<MemoryPath>();
private MemoryDirectory(final String name, final MemoryDirectory parent)
{
super(name, parent);
}
public void add(final MemoryPath p)
{
contents.add(p);
p.parent = this;
}
@Override
protected MemoryPath find(String path)
{
if (super.find(path) != null) {
return super.find(path);
}
while (path.startsWith("/")) {
path = path.substring(1);
}
if (!path.contains("/")) {
for (final MemoryPath p : contents) {
if (p.name.equals(path)) {
return p;
}
}
return null;
}
final String nextName = path.substring(0, path.indexOf("/"));
final String rest = path.substring(path.indexOf("/"));
for (final MemoryPath p : contents) {
if (p.name.equals(nextName)) {
return p.find(rest);
}
}
return null;
}
@Override
protected void getattr(final StatWrapper stat)
{
stat.setMode(NodeType.DIRECTORY);
}
private void mkdir(final String lastComponent)
{
contents.add(new MemoryDirectory(lastComponent, this));
}
public void mkfile(final String lastComponent)
{
contents.add(new MemoryFile(lastComponent, this));
}
public void read(final DirectoryFiller filler)
{
for (final MemoryPath p : contents) {
filler.add(p.name);
}
}
}
private final class MemoryFile extends MemoryPath
{
private ByteBuffer contents = ByteBuffer.allocate(0);
private MemoryFile(final String name, final MemoryDirectory parent)
{
super(name, parent);
}
@Override
protected void getattr(final StatWrapper stat)
{
stat.setMode(NodeType.FILE);
stat.size(contents.capacity());
}
private int read(final ByteBuffer buffer, final long size, final long offset)
{
final int bytesToRead = (int) Math.min(contents.capacity() - offset, size);
final byte[] bytesRead = new byte[bytesToRead];
contents.get(bytesRead, (int) offset, bytesToRead);
buffer.put(bytesRead);
contents.position(0); // Rewind
return bytesToRead;
}
private void truncate(final long size)
{
if (size < contents.capacity()) {
// Need to create a new, smaller buffer
final ByteBuffer newContents = ByteBuffer.allocate((int) size);
final byte[] bytesRead = new byte[(int) size];
contents.get(bytesRead);
newContents.put(bytesRead);
contents = newContents;
}
}
private int write(final ByteBuffer buffer, final long bufSize, final long writeOffset)
{
final int maxWriteIndex = (int) (writeOffset + bufSize);
if (maxWriteIndex > contents.capacity()) {
// Need to create a new, larger buffer
final ByteBuffer newContents = ByteBuffer.allocate(maxWriteIndex);
newContents.put(contents);
contents = newContents;
}
final byte[] bytesToWrite = new byte[(int) bufSize];
buffer.get(bytesToWrite, 0, (int) bufSize);
contents.position((int) writeOffset);
contents.put(bytesToWrite);
contents.position(0); // Rewind
return (int) bufSize;
}
}
private abstract class MemoryPath
{
private final String name;
private MemoryDirectory parent;
private MemoryPath(final String name, final MemoryDirectory parent)
{
this.name = name;
this.parent = parent;
}
private void delete()
{
if (parent != null) {
parent.contents.remove(this);
parent = null;
}
}
protected MemoryPath find(String path)
{
while (path.startsWith("/")) {
path = path.substring(1);
}
if (path.equals(name) || path.isEmpty()) {
return this;
}
return null;
}
protected abstract void getattr(StatWrapper stat);
}
public static void main(final String... args)
{
if (args.length != 1) {
System.err.println("Usage: MemoryFS <mountpoint>");
System.exit(1);
}
try {
new MemoryFS().log(true).mount(args[0]);
}
catch (final Throwable e) {
System.err.println(e);
}
}
private final MemoryDirectory rootDirectory = new MemoryDirectory("", null);
public MemoryFS()
{
// Nothing
}
@Override
public int access(final String path, final int access)
{
return 0;
}
@Override
public int create(final String path, final ModeWrapper mode, final FileInfoWrapper info)
{
if (getPath(path) != null) {
return -ErrorCodes.EEXIST;
}
final MemoryPath parent = getParentPath(path);
if (parent instanceof MemoryDirectory) {
((MemoryDirectory) parent).mkfile(getLastComponent(path));
return 0;
}
return -ErrorCodes.ENOENT;
}
@Override
public int getattr(final String path, final StatWrapper stat)
{
final MemoryPath p = getPath(path);
if (p != null) {
p.getattr(stat);
return 0;
}
return -ErrorCodes.ENOENT;
}
private String getLastComponent(String path)
{
while (path.substring(path.length() - 1).equals("/")) {
path = path.substring(0, path.length() - 1);
}
if (path.isEmpty()) {
return "";
}
return path.substring(path.lastIndexOf("/") + 1);
}
private MemoryPath getParentPath(final String path)
{
return rootDirectory.find(path.substring(0, path.lastIndexOf("/")));
}
private MemoryPath getPath(final String path)
{
return rootDirectory.find(path);
}
@Override
public int mkdir(final String path, final ModeWrapper mode)
{
if (getPath(path) != null) {
return -ErrorCodes.EEXIST;
}
final MemoryPath parent = getParentPath(path);
if (parent instanceof MemoryDirectory) {
((MemoryDirectory) parent).mkdir(getLastComponent(path));
return 0;
}
return -ErrorCodes.ENOENT;
}
@Override
public int open(final String path, final FileInfoWrapper info)
{
return 0;
}
@Override
public int read(final String path, final ByteBuffer buffer, final long size, final long offset, final FileInfoWrapper info)
{
final MemoryPath p = getPath(path);
if (p == null) {
return -ErrorCodes.ENOENT;
}
if (!(p instanceof MemoryFile)) {
return -ErrorCodes.EISDIR;
}
return ((MemoryFile) p).read(buffer, size, offset);
}
@Override
public int readdir(final String path, final DirectoryFiller filler)
{
final MemoryPath p = getPath(path);
if (p == null) {
return -ErrorCodes.ENOENT;
}
if (!(p instanceof MemoryDirectory)) {
return -ErrorCodes.ENOTDIR;
}
((MemoryDirectory) p).read(filler);
return 0;
}
@Override
public int rename(final String path, final String newName)
{
final MemoryPath p = getPath(path);
if (p == null) {
return -ErrorCodes.ENOENT;
}
final MemoryPath newParent = getParentPath(newName);
if (newParent != null) {
return -ErrorCodes.ENOENT;
}
if (!(newParent instanceof MemoryDirectory)) {
return -ErrorCodes.ENOTDIR;
}
p.delete();
((MemoryDirectory) newParent).add(p);
return 0;
}
@Override
public int rmdir(final String path)
{
final MemoryPath p = getPath(path);
if (p == null) {
return -ErrorCodes.ENOENT;
}
if (!(p instanceof MemoryDirectory)) {
return -ErrorCodes.ENOTDIR;
}
p.delete();
return 0;
}
@Override
public int truncate(final String path, final long offset)
{
final MemoryPath p = getPath(path);
if (p == null) {
return -ErrorCodes.ENOENT;
}
if (!(p instanceof MemoryFile)) {
return -ErrorCodes.EISDIR;
}
((MemoryFile) p).truncate(offset);
return 0;
}
@Override
public int unlink(final String path)
{
final MemoryPath p = getPath(path);
if (p == null) {
return -ErrorCodes.ENOENT;
}
p.delete();
return 0;
}
@Override
public int write(final String path, final ByteBuffer buf, final long bufSize, final long writeOffset,
final FileInfoWrapper wrapper)
{
final MemoryPath p = getPath(path);
if (p == null) {
return -ErrorCodes.ENOENT;
}
if (!(p instanceof MemoryFile)) {
return -ErrorCodes.EISDIR;
}
return ((MemoryFile) p).write(buf, bufSize, writeOffset);
}
}
但问题出现了。
如果我没错,FUSE会在列出目录时加载文件内容。
这意味着即使用户没有打开文件,也必须完全加载文件。
所以第一个问题是:如何“按需”加载文件而不使用代理模式?(谁将整个文件系统加载到ram中?)
虽然第一个问题出在远程资源和保险丝机器之间,但第二个问题是前进一步,涉及保险丝机和台式电脑。
由于我在谈论远程资源,这意味着在与台式机共享之前,保险丝必须下载它们。 当然,这将导致2次下载同一资源,一次由保险丝启动,另一次由台式机启动。
这是第二个问题:有没有办法让我的保险丝机器在打开文件时返回类似桌面电脑的链接?(这样桌面电脑会要求提供文件,保险丝机为它提供了一个“链接”,它开始直接从远程资源下载,避免了2次下载之一。
我知道文本有点混乱,但我对这个问题比文本本身更加困惑,所以如果我无法更好地解释我需要的内容,我会道歉但我真的不喜欢我知道怎么问!