在我的Java代码中,我使用FileVisitor遍历文件系统并创建Paths结构,然后将其转换为json对象以便在html中呈现。
在Windows上运行它甚至可以运行Linux文件系统,在Linux上针对同一个(现在是本地的)文件系统运行它在路径上调用toString()时无法正确呈现特殊字符
即Windows调试输出
CreateFolderTree:createJsonData:SEVERE: AddingNode(1):Duarte Lôbo- Requiem
和html显示为
Duarte Lôbo- Requiem
但是linux调试输出给出了
CreateFolderTree:createJsonData:SEVERE: AddingNode(1):Duarte L??bo- Requiem
和html显示为两个带有问号的黑色钻石而不是ôchar
为什么会发生这种情况,Paths由FileVisitor类提供,所以必须正确构造(即我自己不是黑客攻击),然后我只是在路径上调用toString()。
这是一个字体问题,我在linux系统上遇到了一些字体问题,但在这里我只是将字符串返回到html,所以看不到连接。
可能是编码问题,但我看不到我明确设置编码的地方
下面的大量代码,调试显示linux的无效输出是在createJsonData()方法
中编辑:我修复了日志记录问题,以便输出写为UTF-8
FileHandler fe = new FileHandler(logFileName, LOG_SIZE_IN_BYTES, 10, true);
fe.setEncoding(StandardCharsets.UTF_8.name());
所以我们现在看到Windows输出正确
CreateFolderTree:createJsonData:SEVERE: AddingNode(1):Duarte Lôbo- Requiem
但Linux正在输出
CreateFolderTree:createJsonData:SEVERE: AddingNode(1):Duarte L��bo- Requiem
如果我在HexEditor中查看它,它会为L��bo
提供此输出4C EF BF BD EF BF BD 62 6F
编辑:部分解决方案
我遇到了What exactly is sun.jnu.encoding?
并发现建议添加此
-Dsun.jnu.encoding=UTF-8
它的工作文件现在显示好了
不幸的是,如果用户随后点击了这样的文件并发送回服务器,我现在就会收到此错误
java.lang.NullPointerException
at java.base/sun.nio.fs.UnixPath.normalizeAndCheck(Unknown Source)
at java.base/sun.nio.fs.UnixPath.<init>(Unknown Source)
at java.base/sun.nio.fs.UnixFileSystem.getPath(Unknown Source)
at java.base/java.nio.file.Paths.get(Unknown Source)
at com.jthink.songkong.server.callback.ServerFixSongs.configureFileMapping(ServerFixSongs.java:59)
at com.jthink.songkong.server.callback.ServerFixSongs.startTask(ServerFixSongs.java:88)
at com.jthink.songkong.server.CmdRemote.lambda$null$36(CmdRemote.java:107)
我尝试在添加中添加-Dfile.encoding=UTF-8
或者代替jnu选项而没有帮助,jnu选项是我需要的。
我不必添加这个未记录的sun-jnu-encoding选项,所以似乎服务器在某种程度上被破坏了?
代码
import com.google.common.base.Strings;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.jthink.songkong.analyse.analyser.Counters;
import com.jthink.songkong.analyse.general.Errors;
import com.jthink.songkong.cmdline.SongKong;
import com.jthink.songkong.fileloader.RecycleBinFolderNames;
import com.jthink.songkong.server.fs.Data;
import com.jthink.songkong.server.fs.PathWalker2;
import com.jthink.songkong.server.fs.State;
import com.jthink.songkong.ui.MainWindow;
import com.jthink.songkong.ui.progressdialog.FixSongsCounters;
import spark.Request;
import spark.Response;
import java.io.IOException;
import java.net.UnknownHostException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
/**
* Count the number of files that can be loaded, for information purposes only
*/
public class CreateFolderTree
{
private Path treeRoot;
Set<Path> keys = new HashSet<Path>();
public static class VisitFolder
extends SimpleFileVisitor<Path>
{
private Set<Path> keys;
private Integer maxDepth;
private int depth;
public VisitFolder(Set<Path> keys, Integer maxDepth)
{
this.keys=keys;
this.maxDepth = maxDepth;
}
/**
*
* @param dir
* @param attrs
* @return
* @throws IOException
*/
/*
* Ignore some dirs
* @param dir
* @param attrs
* @return
* @throws IOException
*/
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException
{
try
{
if (dir.toFile().getName().equals(".AppleDouble"))
{
return FileVisitResult.SKIP_SUBTREE;
}
else if (dir.toString().equals("/proc"))
{
return FileVisitResult.SKIP_SUBTREE;
}
else if (dir.toString().equals("/dev"))
{
return FileVisitResult.SKIP_SUBTREE;
}
else if (RecycleBinFolderNames.isMatch(dir.toFile().getName()))
{
MainWindow.logger.log(Level.SEVERE, "Ignoring " + dir.toString());
return FileVisitResult.SKIP_SUBTREE;
}
else if (dir.toString().toLowerCase().endsWith(".tar"))
{
return FileVisitResult.SKIP_SUBTREE;
}
depth++;
if(depth > maxDepth)
{
depth--;
return FileVisitResult.SKIP_SUBTREE;
}
keys.add(dir);
return super.preVisitDirectory(dir, attrs);
}
catch(IOException e)
{
MainWindow.logger.warning("Unable visit dir:"+dir + ":"+e.getMessage());
return FileVisitResult.SKIP_SUBTREE;
}
}
/**
*
* Tar check due to http://stackoverflow.com/questions/14436032/why-is-java-7-files-walkfiletree-throwing-exception-on-encountering-a-tar-file-o/14446993#14446993
* SONGKONG-294:Ignore exceptions if file is not readable
*
* @param file
* @param exc
* @return
* @throws IOException
*/
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException
{
if (file.toString().endsWith(".tar")) {
//We dont log to reports as this is a bug in Java that we are handling not a problem in SongKong
MainWindow.logger.log(Level.SEVERE, exc.getMessage());
return FileVisitResult.CONTINUE;
}
try
{
FileVisitResult result = super.visitFileFailed(file, exc);
return result;
}
catch(IOException e)
{
MainWindow.logger.warning("Unable to visit file:"+file + ":"+e.getMessage());
return FileVisitResult.CONTINUE;
}
}
/**
* SONGKONG-294:Ignore exception if folder is not readable
*
* @param dir
* @param exc
* @return
* @throws IOException
*/
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
throws IOException
{
depth--;
try
{
FileVisitResult result = super.postVisitDirectory(dir, exc);
return result;
}
catch(IOException e)
{
MainWindow.logger.warning("Unable to count files in dir(2):"+dir);
return FileVisitResult.CONTINUE;
}
}
}
public CreateFolderTree(Path treeRoot)
{
this.treeRoot = treeRoot;
}
public String start(int depth)
{
VisitFolder visitFolder;
try
{
if(treeRoot==null)
{
for (Path path : FileSystems.getDefault().getRootDirectories())
{
visitFolder = new VisitFolder(keys, depth);
Files.walkFileTree(path, visitFolder);
}
}
else
{
visitFolder = new VisitFolder(keys, depth);
Files.walkFileTree(treeRoot, visitFolder);
}
PathWalker2 pw = new PathWalker2();
for (Path key : keys)
{
//SONGKONG-505: Illegal character in Filepath problem prevented reportFile creation
try
{
pw.addPath(key);
}
catch (InvalidPathException ipe)
{
MainWindow.logger.log(Level.SEVERE, ipe.getMessage(), ipe);
}
}
Gson gson = new GsonBuilder().create();
return gson.toJson(createJsonData(pw.getRoot()));
}
catch (Exception e)
{
handleException(e);
}
return "";
}
public void handleException(Exception e)
{
MainWindow.logger.log(Level.SEVERE, "Unable to count files:"+e.getMessage(), e);
Errors.addError("Unable to count files:"+e.getMessage());
MainWindow.logger.log(Level.SEVERE, e.getMessage());
Counters.getErrors().getCounter().incrementAndGet();
SongKong.refreshProgress(FixSongsCounters.SONGS_ERRORS);
}
/**
* Add this node and recursively its children, returning json data representing the tree
*
* @param node
* @return
*/
private Data createJsonData(PathWalker2.Node node)
{
Data data = new Data();
if(node.getFullPath()!=null)
{
data.setId(node.getFullPath().toString());
if(node.getFullPath().getFileName()!=null)
{
MainWindow.logger.severe("AddingNode(1):"+node.getFullPath().getFileName().toString());
data.setText(node.getFullPath().getFileName().toString());
}
else
{
MainWindow.logger.severe("AddingNode(2):"+node.getFullPath().toString());
data.setText(node.getFullPath().toString());
}
}
else
{
try
{
data.setText(java.net.InetAddress.getLocalHost().getHostName());
data.setId("#");
State state = new State();
state.setOpened(true);
data.setState(state);
}
catch(UnknownHostException uhe)
{
data.setText("Server");
}
}
//Recursively add each child folder of this node
Map<String, PathWalker2.Node> children = node.getChildren();
if(children.size()>0)
{
data.setChildren(new ArrayList<>());
for (Map.Entry<String, PathWalker2.Node> next : children.entrySet())
{
data.getChildren().add(createJsonData(next.getValue()));
}
}
else
{
data.setBooleanchildren(true);
}
return data;
}
public static String createFolderJsonData(Request request, Response response)
{
if(Strings.nullToEmpty(request.queryParams("id")).equals("#"))
{
CreateFolderTree cft = new CreateFolderTree(null);
String treeData = cft.start(1).replace("booleanchildren", "children");
return treeData;
}
else
{
CreateFolderTree cft = new CreateFolderTree(Paths.get(request.queryParams("id")));
String treeData = cft.start(2 ).replace("booleanchildren", "children");
return treeData;
}
}
}
import java.nio.file.Path;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
/** Constructs a tree of folders based on a list of filepaths
*
* i.e a give it a list if all folder that contain files that have been modified and it creates a hierachy
* that can then be used to generate a data structure for use by jstree
*
*/
public class PathWalker2
{
private final Node root;
public PathWalker2()
{
root = new Node();
}
public Node getRoot()
{
return root;
}
/**
* Represent a node on the tree (may/not have children)
*/
public static class Node
{
//Keyed on name and node
private final Map<String, Node> children = new TreeMap<>();
private Path fullPath;
public Node addChild(String name)
{
if (children.containsKey(name))
return children.get(name);
Node result = new Node();
children.put(name, result);
return result;
}
public Map<String, Node> getChildren()
{
return Collections.unmodifiableMap(children);
}
public void setFullPath(Path fullPath)
{
this.fullPath = fullPath;
}
public Path getFullPath()
{
return fullPath;
}
}
/**
* @param path
*/
public void addPath(Path path)
{
Node node = root.addChild((path.getRoot().toString().substring(0, path.getRoot().toString().length() - 1)));
//For each segment of the path add as child if not already added
for (int i = 0; i < path.getNameCount(); i++)
{
node = node.addChild(path.getName(i).toString());
}
//Set full path of this node
node.setFullPath(path);
}
}
答案 0 :(得分:1)
对于html,您需要设置符合您需要的正确字符集,或者更好地坚持使用ASCII,并对所有非ASCII字符使用html编码。 即使没有为您的html显示定义特定的字符集,这也有效。
答案 1 :(得分:1)
您的调试输出似乎经历了字符集之间的多次转换。您发送到控制台的文本似乎使用UTF-8
作为编码转换为字节,从而导致从ô
到ô
的转换。然后似乎是使用系统的字符集将byte
- 数据转换回字符的另一种转换。 Windows控制台使用cp1252
作为字符集,而Linux在每个安装基础上具有不同的设置。在您的情况下,似乎是ASCII导致转换为UTF-8编码数据的两个?
,因为这些字节具有未在ASCII中定义的值。
我不知道您正在使用的日志框架或您正在使用的Logger的具体设置,所以我无法告诉您如何解决这个问题,但对于Linux版本,您可能会检查控制台的字符集并将其更改为UTF-8以查看是否具有所需的效果。
答案 2 :(得分:1)
因此,与编码问题一样,这需要进行大量的调试工作。不仅有很多不同的因素影响它,它们也会在不同的时间影响它,所以第一个任务始终是检查哪里首先出错。
正如�
的处理显示的那样,一旦出错,它可能会出现更多错误,如果你尝试从最终结果开始调试,它就会出现问题。比如从腐烂的洋葱上剥下层。
在这种情况下,问题的根源在操作系统区域设置中,该区域设置设置为POSIX
。这个旧标准使您的操作系统像70年代一样,具有ASCII
编码和其他过时的细节。 ASCII
编码将阻止操作系统理解文件名,文本或包含更多异域字符的任何内容。这会导致奇怪的问题,因为JVM本身就做得很好,但是只要它与操作系统通信(打印到文本文件,要求打开具有特定名称的文件),就有可能因为操作系统而损坏我不明白JVM在说什么。
就像有人在和你说话,每隔一段时间他就会在那里说一句中文。你用英语写下他用英语写的东西,但是你用'#34;取代的每一个中文单词都没有理解???&#34;。
区域设置(在/etc/default/locale
中)通常包含合理的默认值,但正如我们在此处看到的那样,您始终无法信任。对于任何现代系统,您都需要像en_EN.UTF-8
这样的区域设置值。你永远不希望在这个时代看到POSIX
。