มี API เพื่อรับ classpath ทรัพยากร (เช่นสิ่งที่ฉันได้รับจากClass.getResource(String)
) เป็นjava.nio.file.Path
? เป็นการดีที่ฉันต้องการใช้Path
API ใหม่ที่แปลกใหม่กับแหล่งข้อมูล classpath
มี API เพื่อรับ classpath ทรัพยากร (เช่นสิ่งที่ฉันได้รับจากClass.getResource(String)
) เป็นjava.nio.file.Path
? เป็นการดีที่ฉันต้องการใช้Path
API ใหม่ที่แปลกใหม่กับแหล่งข้อมูล classpath
คำตอบ:
อันนี้ใช้ได้สำหรับฉัน:
return Paths.get(ClassLoader.getSystemResource(resourceName).toURI());
Thread.currentThread().getContextClassLoader().getResource(resourceName).toURI()
คาดเดาว่าสิ่งที่คุณต้องการจะทำคือเรียกใช้ Files.lines (... ) บนทรัพยากรที่มาจาก classpath - อาจมาจากภายใน jar
เนื่องจาก Oracle ทำให้ความเชื่อของเมื่อ Path คือ Path โดยไม่ให้ getResource ส่งคืนพา ธ ที่ใช้งานได้หากมันอยู่ในไฟล์ jar สิ่งที่คุณต้องทำคือ:
Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines();
class.getResource
ต้องใช้เครื่องหมายทับ แต่getSystemResourceAsStream
ไม่สามารถค้นหาไฟล์เมื่อนำหน้าด้วยเครื่องหมายทับ
วิธีแก้ปัญหาทั่วไปส่วนใหญ่มีดังนี้:
interface IOConsumer<T> {
void accept(T t) throws IOException;
}
public static void processRessource(URI uri, IOConsumer<Path> action) throws IOException {
try {
Path p=Paths.get(uri);
action.accept(p);
}
catch(FileSystemNotFoundException ex) {
try(FileSystem fs = FileSystems.newFileSystem(
uri, Collections.<String,Object>emptyMap())) {
Path p = fs.provider().getPath(uri);
action.accept(p);
}
}
}
อุปสรรคหลักคือการจัดการกับสองความเป็นไปได้ทั้งที่มีระบบไฟล์ที่มีอยู่ที่เราควรใช้ แต่ไม่ปิด (เช่นกับfile
URIs หรือที่เก็บโมดูลของ Java 9) หรือต้องเปิดและปิดระบบไฟล์อย่างปลอดภัยด้วยตนเอง (เช่น ไฟล์ zip / jar)
ดังนั้นโซลูชันดังกล่าวข้างต้นสรุปการกระทำจริงในการinterface
จัดการทั้งสองกรณีปิดอย่างปลอดภัยในภายหลังในกรณีที่สองและทำงานจาก Java 7 ถึง Java 10 ตรวจสอบว่ามีระบบไฟล์เปิดอยู่แล้วก่อนที่จะเปิดใหม่ดังนั้น ยังสามารถใช้งานได้ในกรณีที่ส่วนประกอบอื่นของแอปพลิเคชันของคุณได้เปิดระบบไฟล์สำหรับไฟล์ zip / jar เดียวกันแล้ว
มันสามารถใช้ในทุกเวอร์ชั่น Java ชื่อข้างต้นเช่นการแสดงเนื้อหาของแพคเกจ ( java.lang
ในตัวอย่าง) เป็นPath
s เช่นนี้:
processRessource(Object.class.getResource("Object.class").toURI(), new IOConsumer<Path>() {
public void accept(Path path) throws IOException {
try(DirectoryStream<Path> ds = Files.newDirectoryStream(path.getParent())) {
for(Path p: ds)
System.out.println(p);
}
}
});
ด้วย Java 8 หรือใหม่กว่าคุณสามารถใช้การแสดงออกแลมบ์ดาหรือการอ้างอิงวิธีการเพื่อแสดงถึงการกระทำที่แท้จริงเช่น
processRessource(Object.class.getResource("Object.class").toURI(), path -> {
try(Stream<Path> stream = Files.list(path.getParent())) {
stream.forEach(System.out::println);
}
});
ทำเช่นเดียวกัน
รุ่นสุดท้ายของระบบโมดูลของ Java 9 ได้ทำลายตัวอย่างโค้ดข้างต้น JRE รุ่นไม่ลงรอยกันส่งกลับเส้นทาง/java.base/java/lang/Object.class
สำหรับในขณะที่มันควรจะเป็นObject.class.getResource("Object.class")
/modules/java.base/java/lang/Object.class
สิ่งนี้สามารถแก้ไขได้โดยการเพิ่มส่วนที่หายไป/modules/
เมื่อรายงานพา ธ พาเรนต์ว่าไม่มีอยู่:
processRessource(Object.class.getResource("Object.class").toURI(), path -> {
Path p = path.getParent();
if(!Files.exists(p))
p = p.resolve("/modules").resolve(p.getRoot().relativize(p));
try(Stream<Path> stream = Files.list(p)) {
stream.forEach(System.out::println);
}
});
จากนั้นจะทำงานอีกครั้งกับทุกรุ่นและวิธีการเก็บข้อมูล
ปรากฎว่าคุณสามารถทำได้ด้วยความช่วยเหลือของผู้ให้บริการระบบไฟล์ซิปในตัว อย่างไรก็ตามการส่งผ่าน URI ทรัพยากรโดยตรงเพื่อPaths.get
ไม่ทำงาน แต่ก่อนอื่นต้องสร้างระบบไฟล์ zip สำหรับ jar URI โดยไม่มีชื่อรายการจากนั้นอ้างถึงรายการในระบบไฟล์นั้น:
static Path resourceToPath(URL resource)
throws IOException,
URISyntaxException {
Objects.requireNonNull(resource, "Resource URL cannot be null");
URI uri = resource.toURI();
String scheme = uri.getScheme();
if (scheme.equals("file")) {
return Paths.get(uri);
}
if (!scheme.equals("jar")) {
throw new IllegalArgumentException("Cannot convert to Path: " + uri);
}
String s = uri.toString();
int separator = s.indexOf("!/");
String entryName = s.substring(separator + 2);
URI fileURI = URI.create(s.substring(0, separator));
FileSystem fs = FileSystems.newFileSystem(fileURI,
Collections.<String, Object>emptyMap());
return fs.getPath(entryName);
}
ปรับปรุง:
มีการชี้ให้เห็นอย่างถูกต้องว่ารหัสด้านบนมีทรัพยากรรั่วไหลเนื่องจากรหัสเปิดวัตถุ FileSystem ใหม่ แต่ไม่เคยปิด วิธีที่ดีที่สุดคือการส่งผ่านวัตถุผู้ปฏิบัติงานที่เหมือนผู้บริโภคเช่นเดียวกับที่ Holger ตอบ เปิด ZipFS FileSystem นานพอที่ผู้ปฏิบัติงานจะทำทุกอย่างที่จำเป็นต้องทำกับ Path (ตราบใดที่ผู้ปฏิบัติงานไม่พยายามเก็บวัตถุ Path ไว้ใช้ในภายหลัง) จากนั้นปิด FileSystem
newFileSystem
สามารถนำไปสู่หลายทรัพยากรที่แขวนรอบเปิดตลอดกาล แม้ว่าภาคผนวก @raisercostin หลีกเลี่ยงข้อผิดพลาดเมื่อพยายามที่จะสร้างระบบไฟล์ที่สร้างไว้แล้วถ้าคุณพยายามที่จะใช้กลับมาคุณจะได้รับPath
ClosedFileSystemException
@ Holger ตอบสนองได้ดีสำหรับฉัน
FileSystem
ฉันจะไม่ปิด หากคุณโหลดทรัพยากรจาก Jar และจากนั้นสร้างสิ่งที่จำเป็นFileSystem
- FileSystem
จะช่วยให้คุณสามารถโหลดทรัพยากรอื่น ๆ จาก Jar เดียวกันได้ นอกจากนี้เมื่อคุณสร้างใหม่FileSystem
คุณก็สามารถลองโหลดทรัพยากรอีกครั้งโดยใช้และการดำเนินงานโดยอัตโนมัติจะใช้ใหม่Paths.get(Path)
FileSystem
#getPath(String)
วิธีการกับFileSystem
วัตถุ
ฉันเขียนวิธีใช้ตัวช่วยเล็ก ๆ เพื่ออ่านPaths
จากแหล่งข้อมูลในห้องเรียนของคุณ มันค่อนข้างสะดวกในการใช้เพราะต้องการเพียงการอ้างอิงคลาสที่คุณเก็บทรัพยากรของคุณรวมถึงชื่อของทรัพยากรเอง
public static Path getResourcePath(Class<?> resourceClass, String resourceName) throws URISyntaxException {
URL url = resourceClass.getResource(resourceName);
return Paths.get(url.toURI());
}
คุณไม่สามารถสร้าง URI จากแหล่งข้อมูลภายในไฟล์ jar คุณสามารถเขียนมันลงในไฟล์ชั่วคราวแล้วใช้มัน (java8):
Path path = File.createTempFile("some", "address").toPath();
Files.copy(ClassLoader.getSystemResourceAsStream("/path/to/resource"), path, StandardCopyOption.REPLACE_EXISTING);
อ่านไฟล์จากโฟลเดอร์ทรัพยากรโดยใช้ NIO ใน java8
public static String read(String fileName) {
Path path;
StringBuilder data = new StringBuilder();
Stream<String> lines = null;
try {
path = Paths.get(Thread.currentThread().getContextClassLoader().getResource(fileName).toURI());
lines = Files.lines(path);
} catch (URISyntaxException | IOException e) {
logger.error("Error in reading propertied file " + e);
throw new RuntimeException(e);
}
lines.forEach(line -> data.append(line));
lines.close();
return data.toString();
}
คุณจำเป็นต้องกำหนดระบบแฟ้มไปยังทรัพยากรอ่านจากไฟล์ขวดดังกล่าวในhttps://docs.oracle.com/javase/8/docs/technotes/guides/io/fsp/zipfilesystemprovider.html ฉันประสบความสำเร็จในการอ่านทรัพยากรจากไฟล์ jar ด้วยรหัสด้านล่าง:
Map<String, Object> env = new HashMap<>();
try (FileSystem fs = FileSystems.newFileSystem(uri, env)) {
Path path = fs.getPath("/path/myResource");
try (Stream<String> lines = Files.lines(path)) {
....
}
}
Paths.get(URI)
แล้ว'URL.toURI (), and last
getResource ()URL
`ซึ่งส่งกลับ คุณอาจจะเชื่อมโยงเหล่านั้นเข้าด้วยกัน ยังไม่ได้ลอง