我遇到Zip File System Provider的问题:如果zip文件位于远程驱动器上(映射与否似乎无关),则虚拟文件系统是只读的,尽管文件本身不是。我写了一个最小的示例代码:
public static void main(String[] args) throws IOException {
File workingDir = new File(args[0]);
File source = new File(workingDir, "in.zip");
File target = new File(workingDir, "out.zip");
Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
try (FileSystem zipfs = FileSystems.newFileSystem(target.toPath(), null)) {
Path pathInZipfile = zipfs.getPath("test.xml");
System.out.println("zipfile writable: " + target.canWrite());
System.out.println("zipFS writable: " + !zipfs.isReadOnly());
Files.delete(pathInZipfile);
System.out.println("File successfully deleted");
} catch (IOException e) {
e.printStackTrace();
}
}
如果workingDir是本地目录,一切正常。但是,如果它是(映射的)远程驱动器,我得到:
zipfile writable: true
zipFS writable: false
Exception in thread "main" java.nio.file.ReadOnlyFileSystemException
at com.sun.nio.zipfs.ZipFileSystem.checkWritable(ZipFileSystem.java:155)
at com.sun.nio.zipfs.ZipFileSystem.deleteFile(ZipFileSystem.java:1335)
at com.sun.nio.zipfs.ZipPath.delete(ZipPath.java:655)
at com.sun.nio.zipfs.ZipFileSystemProvider.delete(ZipFileSystemProvider.java:206)
at java.nio.file.Files.delete(Unknown Source)
at zipfs.ZipFS.main(ZipFS.java:23)
我做错了吗?这不可能吗?有解决方法吗?
答案 0 :(得分:0)
我遇到了同样的事情,我查看了JDK代码。
在ZipFileSystem.java中有三个相关的行:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<ImageView
android:src="@drawable/mv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
zfPath是一个Path对象。 Windows FileSystem提供程序中的某些内容阻止对zip存档路径的写访问。似乎没有什么可以做的。
我用作解决方法的是:
只要映射的驱动器在zip文件系统之外的上下文中是可写的,这种方法就可以工作。
答案 1 :(得分:0)
我们遇到了完全相同的问题,并设计了一些野蛮的解决方案。
FileSystems.newFileSystem(Path)
接受 Path
接口,因此可以满足它包装真正的 Path
实例。
这是实现所需 FileSysteProvider.checkAccess()
的解决方案:
private static FileSystem createZip(Path zipPath)
throws Exception
{
var fileSystem = zipPath.getFileSystem();
var provider = fileSystem.provider();
return FileSystems.newFileSystem(
new Path()
{
private Path path(Path path)
{
return this == path ? zipPath : path;
}
@Override
public FileSystem getFileSystem()
{
return new FileSystem()
{
public Set<String> supportedFileAttributeViews()
{
return fileSystem.supportedFileAttributeViews();
}
@Override
public FileSystemProvider provider()
{
return new FileSystemProvider()
{
@Override
public void setAttribute(
Path path,
String attribute,
Object value,
LinkOption... options)
throws IOException
{
provider.setAttribute(path(path), attribute, value, options);
}
@Override
public Map<String, Object> readAttributes(
Path path,
String attributes,
LinkOption... options)
throws IOException
{
return provider.
readAttributes(path(path), attributes, options);
}
@Override
public <A extends BasicFileAttributes> A readAttributes(
Path path,
Class<A> type,
LinkOption... options)
throws IOException
{
return provider.readAttributes(path(path), type, options);
}
@Override
public FileSystem newFileSystem(URI uri, Map<String, ?> env)
throws IOException
{
return provider.newFileSystem(uri, env);
}
@Override
public DirectoryStream<Path> newDirectoryStream(
Path dir,
Filter<? super Path> filter)
throws IOException
{
return provider.newDirectoryStream(dir, filter);
}
@Override
public SeekableByteChannel newByteChannel(
Path path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
return provider.newByteChannel(path(path), options, attrs);
}
@Override
public void move(
Path source,
Path target,
CopyOption... options)
throws IOException
{
provider.move(path(source), path(target), options);
}
@Override
public boolean isSameFile(Path path, Path path2)
throws IOException
{
return provider.isSameFile(path(path), path(path2));
}
@Override
public boolean isHidden(Path path)
throws IOException
{
return provider.isHidden(path(path));
}
@Override
public String getScheme()
{
return provider.getScheme();
}
@Override
public Path getPath(URI uri)
{
return provider.getPath(uri);
}
@Override
public FileSystem getFileSystem(URI uri)
{
return provider.getFileSystem(uri);
}
@Override
public FileStore getFileStore(Path path)
throws IOException
{
return provider.getFileStore(path(path));
}
@Override
public <V extends FileAttributeView> V getFileAttributeView(
Path path,
Class<V> type,
LinkOption... options)
{
return provider.
getFileAttributeView(path(path), type, options);
}
@Override
public void delete(Path path)
throws IOException
{
provider.delete(path(path));
}
@Override
public void createDirectory(Path dir, FileAttribute<?>... attrs)
throws IOException
{
provider.createDirectory(path(dir), attrs);
}
@Override
public void copy(
Path source,
Path target,
CopyOption... options)
throws IOException
{
provider.copy(path(source), path(target), options);
}
@Override
public void checkAccess(Path path, AccessMode... modes)
throws IOException
{
if ((modes != null) &&
(modes.length == 1) &&
(modes[0] == AccessMode.WRITE))
{
return;
}
provider.checkAccess(path(path), modes);
}
};
}
@Override
public WatchService newWatchService()
throws IOException
{
return fileSystem.newWatchService();
}
@Override
public boolean isReadOnly()
{
return false;
}
@Override
public boolean isOpen()
{
return fileSystem.isOpen();
}
@Override
public UserPrincipalLookupService getUserPrincipalLookupService()
{
return fileSystem.getUserPrincipalLookupService();
}
@Override
public String getSeparator()
{
return fileSystem.getSeparator();
}
@Override
public Iterable<Path> getRootDirectories()
{
return fileSystem.getRootDirectories();
}
@Override
public PathMatcher getPathMatcher(String syntaxAndPattern)
{
return fileSystem.getPathMatcher(syntaxAndPattern);
}
@Override
public Path getPath(String first, String... more)
{
return fileSystem.getPath(first, more);
}
@Override
public Iterable<FileStore> getFileStores()
{
return fileSystem.getFileStores();
}
@Override
public void close() throws IOException
{
fileSystem.close();
}
};
}
@Override
public boolean isAbsolute()
{
return zipPath.isAbsolute();
}
@Override
public Path getRoot()
{
return zipPath.getRoot();
}
@Override
public Path getFileName()
{
return zipPath.getFileName();
}
@Override
public Path getParent()
{
return zipPath.getParent();
}
@Override
public int getNameCount()
{
return zipPath.getNameCount();
}
@Override
public Path getName(int index)
{
return zipPath.getName(index);
}
@Override
public Path subpath(int beginIndex, int endIndex)
{
return zipPath.subpath(beginIndex, endIndex);
}
@Override
public boolean startsWith(Path other)
{
return zipPath.startsWith(other);
}
@Override
public boolean endsWith(Path other)
{
return zipPath.endsWith(other);
}
@Override
public Path normalize()
{
return zipPath.normalize();
}
@Override
public Path resolve(Path other)
{
return zipPath.relativize(other);
}
@Override
public Path relativize(Path other)
{
return zipPath.relativize(other);
}
@Override
public URI toUri()
{
return zipPath.toUri();
}
@Override
public Path toAbsolutePath()
{
return zipPath.toAbsolutePath();
}
@Override
public Path toRealPath(LinkOption... options)
throws IOException
{
return zipPath.toRealPath(options);
}
@Override
public WatchKey register(
WatchService watcher,
Kind<?>[] events,
Modifier... modifiers)
throws IOException
{
return zipPath.register(watcher, events, modifiers);
}
@Override
public int compareTo(Path other)
{
return zipPath.compareTo(other);
}
},
Map.of("create", "true"));
}
这更像是一个 hack 但我们认为它解决了原始 ZipFileSystem
答案 2 :(得分:-1)
尝试使用反射将readOnly
中的私人字段ZipFileSystem
设置为false
。