2015-04-02 15 views
5

Non ho lavorato con java a lungo, quindi non sono sicuro di cosa altro cercare. Spero che qualcuno possa indicarmi la giusta direzione.Recupero di risorse per una classe di sottopackage

Obiettivo: Desidero utilizzare una tabella di ricerca, memorizzata come file di testo. Ma non voglio usare percorsi assoluti, dato che alla fine mi piacerebbe mettere in valigia una release ed essere in grado di chiamarla da qualsiasi posizione (il file di testo sarà incluso nella versione compresso).

configurazione attuale: ho messo il file di testo in una cartella denominata "risorse" (perché dalla lettura tutorial su Java, ho avuto l'impressione, questo è dove dovrei metterlo a mantenere una migliore strutturata progetto).

Nella cartella del pacchetto radice ho una classe (MainClass.java) che chiama un'altra classe (LookUpClass.java) in un sotto pacchetto. L'impostazione della cartella è come segue:

  • src
    • java
      • main.package.com
        • subpackage
          • LookUpClass.java
          • PlotterClass.java
        • MainClass.java
    • risorse
      • lookuptables
        • LookUpTable1.txt
        • LookUpTable2.txt

Ho scritto un metodo in LookUpClass.java che recupera una determinata riga dalle mie tabelle di ricerca in risorse. Per recuperare il file e leggere una certa linea, ho usato

// Gets respective line from LUT 
private static String getLineFromLUT(int line) { 
    URL url = LookUpClass.class.getClass().getResource("/LookUpTables/LookUpTable1.txt"); 
    File file = new File(url.toURI()); 
    BufferedReader br = new BufferedReader(new FileReader(file)); 

    for (int i = 0; i < line; ++i) 
     br.readLine(); 

    return br.readLine; 
} 

Nella mia struttura del progetto la cartella "Java" è contrassegnato come "fonte", mentre "risorse" è contrassegnato come, pure, "risorse".

La mia configurazione di prova è molto semplice:

public static void main(String[] args) throws URISyntaxException, IOException { 
    String c = LookUpClass.getLineFromLUT(5); 
    System.out.println("Color of line 5: " + c); 
} 

uscita:

Color of line 5: 0 0 38 

(che è corretto.)

ho aggiunto le stesse linee esatte per PlotterClass.java e funziona bene, anche.

Problema:

Ora, Se provo lo stesso in MainClass.java ottengo un errore con url essendo nullo. Sembra che la cartella risorse/risorse non possa essere trovata.

ho letto attraverso vari messaggi su SO e già provato fuori diverse soluzioni proposte, che tutti falliti finora:

  • Se utilizza LookUpClass.class.getClassLoader().getResource("/LookUpTables/LookUpTable1.txt") entrambe chiamate da MainClass.java e LookUpClass.java sicuro (url è nullo).
  • Ho provato utilizzando seguenti percorsi (tutti non funziona in una delle classi): "lookuptables/LookUpTable1.txt" (rimozione di partenza "/") "/subpackage/LookUpTables/LookUpTable1.txt" " ../ subpackage/LookUpTables/LookUpTable1.txt "
  • Da quando ho usato Idea IntelliJ, ho controllato" Impostazioni> Costruisci, Esecuzione, Distribuzione> Compendio> Schemi di risorse "e aggiunto" * .txt "ai modelli. Niente è cambiato.
  • Se si aggiunge Class c = LookUpClass.class.getClass();, in modalità Debug c è "class.java.lang.Class". Mi aspettavo qualcosa come "main.package.com.subpackage.LookUpClass".
  • A un certo punto ho provato a utilizzare getResourceAsStream(), ma non ho capito come ottenere la mia (ad esempio) quinta linea, quindi l'ho scartata. Sono disposto a leggere su questo, se risolve il mio problema però.

Non ho idea di come risolvere questo problema. E mi rendo conto che a questo punto sto solo provando le cose, senza nemmeno capire perché potrebbe o non potrebbe funzionare. Per me, sembra che LookUpClass.java venga eseguito da una posizione diversa da MainClass.java. Ma la cartella "risorse" e la rispettiva posizione del file di testo non cambiano mai. Come si può trovare il file in un caso, ma non nell'altro?

+0

Tre ulteriori domande: Stai utilizzando un IDE (come Eclipse)? Stai usando uno strumento di costruzione (come Maven)? I tuoi file di testo devono essere aggiornati dall'utente o fanno parte della distribuzione? – Seelenvirtuose

+0

si può cercare di garantire l'oggetto di classe che si sta utilizzando per ottenere il caricatore di classe dalla realtà è caricata ad esempio 'try { \t \t \t \t \t Class.forName (MainClass.class.getName()); \t \t \t \t} catch (finale ClassNotFoundException ex) { \t \t \t \t} ' – muued

+0

@Seelenvirtuose Sì per entrambi: sto usando IntelliJ e il progetto è infatti costruito con Maven. I file di testo devono essere statici e parte della distribuzione. Contengono i codici RGB per una mappa di calore. – fukiburi

risposta

1

Maven ha un standard directory layout . La directory src/main/resources è destinata a tali risorse applicative. Inserisci i tuoi file di testo.

Ora, fondamentalmente, sono due opzioni esattamente dove posizionare i file: file di

  1. La risorsa appartiene ad una classe.

Un esempio per questo è una classe che rappresenta un elemento della GUI (un pannello) che deve anche mostrare alcune immagini.

In questo caso, inserire il file di risorse nella stessa directory (pacchetto) della classe corrispondente. Per esempio.per una classe denominata your.pkg.YourClass posto il file di risorse nella directory your/pkg:

src/main 
+-- java/ 
| +-- your/pkg/ 
| | +-- YourClass.java 
+-- resources/ 
     +-- your/pkg/ 
      +-- resource-file.txt 

Ora caricare la risorsa tramite la classe corrispondente. All'interno della classe your.pkg.YourClass si ha il seguente frammento di codice per il caricamento:

String resource = "resource-file.txt"; // the "file name" without any package or directory 
Class<?> clazz = this.getClass(); // or YourClass.class 
URL resourceUrl = clazz.getResource(resource); 
if (resourceUrl != null) { 
    try (InputStream input = resourceUrl.openStream()) { 
     // load the resource here from the input stream 
    } 
} 

Nota: È inoltre possibile caricare la risorsa tramite il caricatore di classe classe:

String resource = "your/pkg/resource-file.txt"; 
ClassLoader loader = this.getClass().getClassLoader(); // or YourClass.class.getClassLoader() 
URL resourceUrl = loader.getResource(resource); 
if (resourceUrl != null) { 
    try (InputStream input = resourceUrl.openStream()) { 
     // load the resource here from the input stream 
    } 
} 

sceglie, quello che si trova più conveniente.

  1. La risorsa appartiene all'intera applicazione.

In questo caso, è sufficiente posizionare la risorsa direttamente nella directory src/main/resources o in una sottodirectory appropriata. Diamo un'occhiata ad un esempio con il file di ricerca:

src/main/resources/ 
    +-- LookupTables/ 
     +-- LookUpTable1.txt 

È quindi necessario caricare la risorsa tramite una class loader, utilizzando il caricatore del thread corrente classe contesto o il caricatore di classe di applicazione (tutto ciò che è più appropriato - andare a cercare per articoli su questo tema se interessati). Vi mostrerò in entrambi i modi:

String resource = "LookupTables/LookUpTable1.txt"; 
ClassLoader ctxLoader = Thread.currentThread().getContextClassLoader(); 
ClassLoader sysLoader = ClassLoader.getSystemClassLoader(); 
URL resourceUrl = ctxLoader.getResource(resource); // or sysLoader.getResource(resource) 
if (resourceUrl != null) { 
    try (InputStream input = resourceUrl.openStream()) { 
     // load the resource here from the input stream 
    } 
} 

Come primo suggerimento, utilizzare la classe contesto loader del thread corrente. In un'applicazione standalone questo sarà il caricatore di classi di sistema o il caricatore di classi di sistema come genitore. (La distinzione tra questi programmi di caricamento di classe diventerà importante per le librerie che caricano anche le risorse.)

È sempre necessario utilizzare un caricatore di classe per caricare le risorse. In questo modo si rende il caricamento indipendente dal luogo (basta fare attenzione che i file si trovino all'interno del percorso della classe all'avvio dell'applicazione) e si può impacchettare l'intera applicazione in un file JAR che trova ancora le risorse.

+0

Grazie mille per il tuo tempo spiegando tutto e mostrando un esempio!Finalmente ho potuto farlo funzionare sia con il caricatore del contex del thread sia con il caricatore della classe di sistema. Devo ancora provare la tua prima soluzione, ma sto pensando di farlo in seguito, dal momento che le tabelle di ricerca in realtà appartengono solo a/sono utilizzate da questa classe. (Ci scusiamo per la risposta tardiva, siamo andati via per il fine settimana prolungato a causa delle vacanze di Pasqua.) – fukiburi

+0

Stranamente, quando provo il metodo 1, il codice funziona se aggiungo effettivamente il percorso completo della cartella (cioè 'String resource =" il tuo/pkg/resource-file.txt "'), ma non funziona, se uso solo il nome del file da solo ('String resource =" resource-file.txt "'). Ho dimenticato qualcosa? – fukiburi

+0

@fukiburi No, non ti sei perso qualcosa. L'ho fatto! Ho mescolato 'ClassLoader.getResource' e' Class.getResource'. Quando si utilizza un programma di caricamento classi, è necessario specificare anche il percorso (relativo al percorso della classe). Modificherò la mia risposta di conseguenza. – Seelenvirtuose

0

Ho provato a riprodurre il problema in base al MWE fornito, ma non ci sono riuscito. Ho caricato il mio progetto tra cui un pom.xml (lei ha citato è stato utilizzato Maven) qui: http://www.filedropper.com/stackoverflow Questo è ciò che la mia classe di ricerca sembra (anche mostrando come utilizzare il metodo getResourceAsStream):

public class LookUpClass { 

    final static String tableName = "resources/LookUpTables/LookUpTable1.txt"; 

    public static String getLineFromLUT(final int line) { 
     final URL url = LookUpClass.class.getResource(tableName); 
     if (url.toString().startsWith("jar:")) { 
      try (final URLClassLoader loader = URLClassLoader 
        .newInstance(new URL[] { url })) { 
       return getLineFromLUT(
         new InputStreamReader(
           loader.getResourceAsStream(tableName)), line); 
      } catch (final IOException e) { 
       e.printStackTrace(); 
      } 
     } else { 
      return getLineFromLUT(
        new InputStreamReader(
          LookUpClass.class.getResourceAsStream(tableName)), 
        line); 
     } 
     return null; 
    } 

    public static String getLineFromLUT(final Reader reader, final int line) { 
     try (final BufferedReader br = new BufferedReader(reader)) { 
      for (int i = 0; i < line; ++i) 
       br.readLine(); 
      return br.readLine(); 
     } catch (final IOException e) { 
      e.printStackTrace(); 
     } 
     return null; 
    } 
} 
+0

Grazie per aver dedicato tempo per aiutarmi a risolvere il mio problema! Soprattutto la parte su come usare getResourceAsStream() e poi leggere una riga è stata molto utile. – fukiburi