getResourceAsStream devuelve nulo

183

Estoy cargando un archivo de texto desde un paquete en un JAR compilado de mi proyecto Java. La estructura de directorio relevante es la siguiente:

/src/initialization/Lifepaths.txt

Mi código carga un archivo llamando Class::getResourceAsStreampara devolver a InputStream.

public class Lifepaths {
    public static void execute() {
        System.out.println(Lifepaths.class.getClass().
            getResourceAsStream("/initialization/Lifepaths.txt"));
    }

    private Lifepaths() {}

    //This is temporary; will eventually be called from outside
    public static void main(String[] args) {execute();}
}

La impresión siempre se imprimirá null, no importa lo que use. No estoy seguro de por qué lo anterior no funcionaría, así que también he intentado:

  • "/src/initialization/Lifepaths.txt"
  • "initialization/Lifepaths.txt"
  • "Lifepaths.txt"

Ninguno de estos trabajos. Hasta ahora he leído numerosas preguntas sobre el tema, pero ninguna de ellas ha sido útil, por lo general, solo dicen que cargue archivos usando la ruta raíz, lo que ya estoy haciendo. Eso, o simplemente carga el archivo desde el directorio actual (solo carga filename), que también he intentado. El archivo se está compilando en el JAR en la ubicación apropiada con el nombre apropiado.

¿Cómo puedo solucionar esto?

Albahaca Bourque
fuente
3
¿Has comprobado que realmente está en el archivo jar? ¿Has revisado la carcasa del archivo?
Jon Skeet
@ JonSkeet De hecho, se está compilando en el archivo JAR en la ubicación adecuada, y el caso es correcto.
1
@greedybuddha Aunque no puedo invocar eso desde un contexto estático, puedo invocarlo usando Lifepaths.class. Dicho esto, ¿por qué getClassLoader()permite que funcione? (¡Además, siéntase libre de publicar una respuesta!)
Puedes mostrar Lifepaths.getClass()? No existe tal método estático definido en Object ...
Puce
1
Eche un vistazo a esta respuesta y vea si puede hacerlo funcionar usando getResource(String). Por cierto, siempre he tenido problemas para que cualquiera de los dos trabaje en un staticcontexto. El problema es básicamente que el cargador de clases obtenido es el destinado a las clases J2SE. Debe obtener acceso al cargador de clases de contexto destinado a la aplicación misma.
Andrew Thompson

Respuestas:

159

Lifepaths.class.getClass().getResourceAsStream(...) carga recursos usando el cargador de clases del sistema, obviamente falla porque no ve sus JAR

Lifepaths.class.getResourceAsStream(...) carga recursos utilizando el mismo cargador de clases que cargó la clase Lifepaths y debería tener acceso a los recursos en sus JAR

hoaz
fuente
129129
Solo para agregar, al invocar getResourceAsStream (nombre), el nombre debe comenzar con "/". No estoy seguro de si esto es necesario, pero tengo un problema sin él.
David
3
He estado jodiendo con esto desde las 8 am de esta mañana de ayer. Me salvó. También necesitaba una barra inclinada para que funcione.
kyle
44
También tenga en cuenta que la fuente deseada puede estar fuera de la jerarquía de paquetes. En este caso, deberá usar "../" en su ruta para subir un nivel y luego bajar a otra rama de ruta para llegar a su recurso.
Zon
3
@David: creo que es necesario (al inicio '/'), de lo contrario, busca en relación con el paquete Lifepaths.class
Mz A
55
Solo para agregar información, debe agregar un /antes de su ruta si su archivo está en un directorio diferente; por ejemplo initialization/Lifepaths.txt. Si la ruta del archivo es la misma de su clase (pero bajo los recursos como directorio principal), puede poner el nombre del archivo sin ninguno /. Por ejemplo, si su clase tiene la siguiente ruta src/main/java/paths/Lifepaths.java, su archivo debe tener esta ruta src/main/resources/paths/Lifepaths.txt.
Dwhitz
60

Las reglas son las siguientes:

  1. verifique la ubicación del archivo que desea cargar dentro del JAR (y, por lo tanto, también asegúrese de que realmente se haya agregado al JAR)
  2. use una ruta absoluta: la ruta comienza en la raíz del JAR
  3. use una ruta relativa: la ruta comienza en el directorio del paquete de la clase a la que llama getResource / getResoucreAsStream

Y prueba:

Lifepaths.class.getResourceAsStream("/initialization/Lifepaths.txt")

en vez de

Lifepaths.class.getClass().getResourceAsStream("/initialization/Lifepaths.txt")

(no estoy seguro si hace la diferencia, pero el primero usará el ClassLoader / JAR correcto, mientras que no estoy seguro con el último)

Pardo rojizo
fuente
2
Ya he hecho las tres cosas. Por favor relee mi pregunta.
De su pregunta no está claro qué es "La estructura de directorio relevante" y si ha verificado si el archivo se encuentra en el JAR y dónde está ubicado (paso 1)
Puce
1
Su comentario sobre el camino relativo finalmente resolvió el problema de mi parte; ¡Gracias!
Paul Bormans
Interesante. Resulta que tuve que hacer "/config.properties" (con una barra) para llegar a él ...
Erk
45

Por lo tanto, hay varias formas de obtener un recurso de un jar y cada una tiene una sintaxis ligeramente diferente en la que la ruta debe especificarse de manera diferente.

La mejor explicación que he visto es este artículo de JavaWorld . Resumiré aquí, pero si quieres saber más, debes consultar el artículo.

Métodos

1) ClassLoader.getResourceAsStream().

Formato: "/" - nombres separados; sin "/" inicial (todos los nombres son absolutos).

Ejemplo: this.getClass().getClassLoader().getResourceAsStream("some/pkg/resource.properties");

2) Class.getResourceAsStream()

Formato: "/" - nombres separados; los "/" iniciales indican nombres absolutos; todos los demás nombres son relativos al paquete de la clase

Ejemplo: this.getClass().getResourceAsStream("/some/pkg/resource.properties");

greedybuddha
fuente
su segundo ejemplo está roto
Janus Troelsen
1
El segundo ejemplo no está roto, como dije en la respuesta, depende de dónde esté ubicado el recurso.
greedybuddha
Además: asegúrese de que su IDE vea el archivo ('some / pkg / resource.properties') actualizando la carpeta de origen.
Eric Duminil
15

No utilice rutas absolutas, haga que sean relativas al directorio de 'recursos' en su proyecto. Código rápido y sucio que muestra el contenido de MyTest.txt desde el directorio 'recursos'.

@Test
public void testDefaultResource() {
    // can we see default resources
    BufferedInputStream result = (BufferedInputStream) 
         Config.class.getClassLoader().getResourceAsStream("MyTest.txt");
    byte [] b = new byte[256];
    int val = 0;
    String txt = null;
    do {
        try {
            val = result.read(b);
            if (val > 0) {
                txt += new String(b, 0, val);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } 
    } while (val > -1);
    System.out.println(txt);
}
Paul Smith
fuente
9

Es posible que desee probar esto para obtener la transmisión, es decir, primero obtenga la URL y luego ábrala como transmisión.

URL url = getClass().getResource("/initialization/Lifepaths.txt"); 
InputStream strm = url.openStream(); 

Una vez tuve una pregunta similar: la lectura del archivo txt del jar falla pero la imagen funciona

MrD
fuente
3
esto es exactamente lo que hace getResourceAsStream ()
fedeb 02 de
8

Parece que hay un problema con el ClassLoader que está utilizando. Use contextClassLoader para cargar la clase. Esto es independiente de si está en un método estático / no estático

Thread.currentThread().getContextClassLoader().getResourceAsStream......

Binita Bharati
fuente
6

Me encontré en un problema similar. Como estoy usando Maven, necesitaba actualizar mi pom.xml para incluir algo como esto:

   ...
</dependencies>
<build>
    <resources>
        <resource>
            <directory>/src/main/resources</directory>
        </resource>
        <resource>
            <directory>../src/main/resources</directory>
        </resource>
    </resources>
    <pluginManagement>
        ...

Tenga en cuenta la etiqueta de recursos allí para especificar dónde está esa carpeta. Si tiene proyectos anidados (como yo), es posible que desee obtener recursos de otras áreas en lugar de solo en el módulo en el que está trabajando. Esto ayuda a reducir el mantenimiento del mismo archivo en cada repositorio si está utilizando datos de configuración similares

plosco
fuente
3

Lo que funcionó para mí fue agregar el archivo My Project/Java Resources/srcy luego usar

this.getClass().getClassLoader().getResourceAsStream("myfile.txt");

No necesitaba agregar explícitamente este archivo a la ruta (agregarlo a /srceso aparentemente)

CommonCoreTawan
fuente
Sintaxis incorrecta de Java
Ilya Kharlamov
2

No sé si es útil, pero en mi caso tenía mi recurso en la carpeta / src / y recibía este error. Luego moví la imagen a la carpeta bin y solucionó el problema.

Leo Ufimtsev
fuente
2

Asegúrese de que su directorio de recursos (por ejemplo, "src") esté en su classpath (asegúrese de que sea un directorio fuente en su ruta de compilación en eclipse).

Asegúrese de que clazz se carga desde el cargador de clases principal.

Luego, para cargar src / initialization / Lifepaths.txt, use

clazz.getResourceAsStream("/initialization/Lifepaths.txt");

Por qué: clazz.getResourcesAsStream(foo)busca foo desde el classpath de clazz , en relación con el directorio en el que vive clazz . El "/" inicial hace que se cargue desde la raíz de cualquier directorio en el classpath de clazz.

A menos que esté en un contenedor de algún tipo, como Tomcat, o esté haciendo algo con ClassLoaders directamente, puede tratar su eclipse / línea de comando classpath como el único classloader classpath.

Bobby Martin
fuente
2

El cargador de clases JVM predeterminada utilizará padre-cargador de clases para cargar recursos en primer lugar: deletegate-parent-classloader.

Lifepaths.class.getClass()El cargador de clases es bootstrap classloader, por getResourceAsStreamlo que buscará solo $ JAVA_HOME, independientemente del usuario proporcionado classpath. Obviamente, Lifepaths.txt no está allí.

Lifepaths.classEl cargador de clases es system classpath classloader, por getResourceAsStreamlo tanto , buscará definido por el usuario classpathy Lifepaths.txt está allí.

Al usar java.lang.Class#getResourceAsStream(String name), el nombre que no comienza con '/' se agregará package namecomo prefijo. Si desea evitar esto, utilice java.lang.ClassLoader#getResourceAsStream. Por ejemplo:

ClassLoader loader = Thread.currentThread().getContextClassLoader();
String resourceName = "Lifepaths.txt";
InputStream resourceStream = loader.getResourceAsStream(resourceName); 
ridox
fuente
2

Mas o menos:

getClass().getResource("/") ~ = Thread.currentThread().getContextClassLoader().getResource(".")

Suponga que la estructura de su proyecto es la siguiente:

├── src
   ├── main
   └── test
   └── test
       ├── java
          └── com
              └── github
                  └── xyz
                      └── proj
                          ├── MainTest.java
                          └── TestBase.java
       └── resources
           └── abcd.txt
└── target
    └── test-classes
        ├── com
        └── abcd.txt
// in MainClass.java
this.getClass.getResource("/") -> "~/proj_dir/target/test-classes/"
this.getClass.getResource(".") -> "~/proj_dir/target/test-classes/com/github/xyz/proj/"
Thread.currentThread().getContextClassLoader().getResources(".") -> "~/proj_dir/target/test-classes/"
Thread.currentThread().getContextClassLoader().getResources("/") ->  null
Shijing Lv
fuente
2

Si está utilizando Maven, asegúrese de que su embalaje sea "frasco" y no "pom".

<packaging>jar</packaging>
Feku279
fuente
0

Lo que realmente necesita es un classPath absoluto completo para el archivo. Entonces, en lugar de adivinarlo, intente averiguar la RAÍZ y luego mueva el archivo a una mejor ubicación base una estructura de archivo <.war> ...

URL test1 = getClass().getResource("/");
URL test2 = getClass().getClassLoader().getResource("/");            
URL test3 = getClass().getClassLoader().getResource("../");

logger.info(test1.getPath()); 
logger.info(test2.getPath());
logger.info(test3.getPath());
ConAim
fuente
0

Lo que funcionó para mí es que coloqué el archivo debajo

src/main/java/myfile.log

y

InputStream is = getClass().getClassLoader().getResourceAsStream("myfile.log");
        
        if (is == null) {
            throw new FileNotFoundException("Log file not provided");
        }
Akash Yellappa
fuente
Se llama a la carpeta de origen src/main/JAVA, obviamente, sus archivos sin código no deben ubicarse aquí.
leyren
-12

@Emracool ... Te sugiero una alternativa. Ya que parece estar intentando cargar un archivo * .txt. Mejor usar en FileInputStream()lugar de este molesto getClass().getClassLoader().getResourceAsStream()o getClass().getResourceAsStream(). Al menos su código se ejecutará correctamente.

Jain
fuente
qué ??? -1 para tal respuesta de trabajo. No importa qué. Pero la solución sugerida anteriormente funcionará con seguridad.
Jain