2012-02-09 111 views
38

Tengo un servidor web ejecutándose con los recursos de Jersey REST y me pregunto cómo obtener una referencia de imagen/png para la etiqueta img de los navegadores; después de enviar un Formulario u obtener una respuesta Ajax. El código de procesamiento de imágenes para agregar gráficos está funcionando, solo necesita devolverlo de alguna manera.Cómo devolver una imagen PNG desde el método de servicio REST de Jersey al navegador

Código:

@POST 
@Path("{fullsize}") 
@Consumes(MediaType.MULTIPART_FORM_DATA) 
@Produces("image/png") 
// Would need to replace void 
public void getFullImage(@FormDataParam("photo") InputStream imageIS, 
         @FormDataParam("submit") String extra) { 

     BufferedImage image = ImageIO.read(imageIS); 

     // .... image processing 
     //.... image processing 

     return ImageIO. .. ? 

} 

Saludos

+0

¿Qué estás tratando de lograr? ¿No puede lograr esto enviando un URI con la ubicación de la imagen? – Perception

+0

Quiero que el usuario obtenga una vista previa de los gráficos seleccionados en la foto antes de hacer un pedido. Ahora veo que esto no se puede hacer con la publicación de AJAX, tendrá que solicitar páginas web como usted dijo apuntando a la imagen procesada. – gorn

Respuesta

79

no estoy convencido de que es una buena idea para devolver datos de imagen en un servicio REST. Ata la memoria del servidor de aplicaciones y el ancho de banda de IO. Es mucho mejor delegar esa tarea en un servidor web adecuado que esté optimizado para este tipo de transferencia. Puede lograr esto enviando un redireccionamiento al recurso de imagen (como una respuesta HTTP 302 con el URI de la imagen). Esto supone, por supuesto, que sus imágenes están organizadas como contenido web.

Dicho esto, si usted decide realmente necesita para transferir datos de imagen de un servicio web, puede hacerlo con el siguiente código (pseudo):

@Path("/whatever") 
@Produces("image/png") 
public Response getFullImage(...) { 

    BufferedImage image = ...; 

    ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
    ImageIO.write(image, "png", baos); 
    byte[] imageData = baos.toByteArray(); 

    // uncomment line below to send non-streamed 
    // return Response.ok(imageData).build(); 

    // uncomment line below to send streamed 
    // return Response.ok(new ByteArrayInputStream(imageData)).build(); 
} 

Añadir en el manejo de excepciones, etc, etc

+0

¡Gracias! Esa es una forma de hacerlo. – gorn

+0

Terminó con una aplicación de servidor PHP que utilizaba cURL para obtener imágenes de este servicio web RESTful Java y las señalaba en la etiqueta de imagen HTML. – gorn

+0

@gorn debe escribir su solución en su respuesta como una edición – kommradHomer

11

me construyeron un método general para que, con las características siguientes:

  • que vuelve "sin modificar" si el archivo no se ha modificado localmente, un Status.NOT_MODIFIED se envía a la persona que llama. Utiliza Apache Commons Lang
  • utilizando un objeto de flujo de archivos en lugar de leer el archivo en sí

Aquí el código:

import org.apache.commons.lang3.time.DateUtils; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 

private static final Logger logger = LoggerFactory.getLogger(Utils.class); 

@GET 
@Path("16x16") 
@Produces("image/png") 
public Response get16x16PNG(@HeaderParam("If-Modified-Since") String modified) { 
    File repositoryFile = new File("c:/temp/myfile.png"); 
    return returnFile(repositoryFile, modified); 
} 

/** 
* 
* Sends the file if modified and "not modified" if not modified 
* future work may put each file with a unique id in a separate folder in tomcat 
* * use that static URL for each file 
* * if file is modified, URL of file changes 
* * -> client always fetches correct file 
* 
*  method header for calling method public Response getXY(@HeaderParam("If-Modified-Since") String modified) { 
* 
* @param file to send 
* @param modified - HeaderField "If-Modified-Since" - may be "null" 
* @return Response to be sent to the client 
*/ 
public static Response returnFile(File file, String modified) { 
    if (!file.exists()) { 
     return Response.status(Status.NOT_FOUND).build(); 
    } 

    // do we really need to send the file or can send "not modified"? 
    if (modified != null) { 
     Date modifiedDate = null; 

     // we have to switch the locale to ENGLISH as parseDate parses in the default locale 
     Locale old = Locale.getDefault(); 
     Locale.setDefault(Locale.ENGLISH); 
     try { 
      modifiedDate = DateUtils.parseDate(modified, org.apache.http.impl.cookie.DateUtils.DEFAULT_PATTERNS); 
     } catch (ParseException e) { 
      logger.error(e.getMessage(), e); 
     } 
     Locale.setDefault(old); 

     if (modifiedDate != null) { 
      // modifiedDate does not carry milliseconds, but fileDate does 
      // therefore we have to do a range-based comparison 
      // 1000 milliseconds = 1 second 
      if (file.lastModified()-modifiedDate.getTime() < DateUtils.MILLIS_PER_SECOND) { 
       return Response.status(Status.NOT_MODIFIED).build(); 
      } 
     } 
    }   
    // we really need to send the file 

    try { 
     Date fileDate = new Date(file.lastModified()); 
     return Response.ok(new FileInputStream(file)).lastModified(fileDate).build(); 
    } catch (FileNotFoundException e) { 
     return Response.status(Status.NOT_FOUND).build(); 
    } 
} 

/*** copied from org.apache.http.impl.cookie.DateUtils, Apache 2.0 License ***/ 

/** 
* Date format pattern used to parse HTTP date headers in RFC 1123 format. 
*/ 
public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz"; 

/** 
* Date format pattern used to parse HTTP date headers in RFC 1036 format. 
*/ 
public static final String PATTERN_RFC1036 = "EEEE, dd-MMM-yy HH:mm:ss zzz"; 

/** 
* Date format pattern used to parse HTTP date headers in ANSI C 
* <code>asctime()</code> format. 
*/ 
public static final String PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy"; 

public static final String[] DEFAULT_PATTERNS = new String[] { 
    PATTERN_RFC1036, 
    PATTERN_RFC1123, 
    PATTERN_ASCTIME 
}; 

Tenga en cuenta que el cambio de configuración regional no parece ser seguro para subprocesos. Creo que es mejor cambiar la configuración regional globalmente. No estoy seguro acerca de los efectos colaterales aunque ...

+3

Puede eliminar una gran parte de su lógica de última modificación mediante RequestEvaluatePreconditions (...) de Jersey, ya que se encargará de analizar y verificar las fechas, y de los etags si los admite. – bramp

6

en cuanto a la respuesta de @Perception, es cierto que consume mucha memoria cuando se trabaja con matrices de bytes, pero también se puede simplemente escribir de nuevo en el outputtream

@Path("/picture") 
public class ProfilePicture { 
    @GET 
    @Path("/thumbnail") 
    @Produces("image/png") 
    public StreamingOutput getThumbNail() { 
    return new StreamingOutput() { 
     @Override 
     public void write(OutputStream os) throws IOException, WebApplicationException { 
     //... read your stream and write into os 
     } 
    }; 
    } 
} 
3

Si usted tiene un número de métodos de recursos de imagen, es bien vale la pena crear un MessageBodyWriter para dar salida al BufferedImage:

@Produces({ "image/png", "image/jpg" }) 
@Provider 
public class BufferedImageBodyWriter implements MessageBodyWriter<BufferedImage> { 
    @Override 
    public boolean isWriteable(Class<?> type, Type type1, Annotation[] antns, MediaType mt) { 
    return type == BufferedImage.class; 
    } 

    @Override 
    public long getSize(BufferedImage t, Class<?> type, Type type1, Annotation[] antns, MediaType mt) { 
    return -1; // not used in JAX-RS 2 
    } 

    @Override 
    public void writeTo(BufferedImage image, Class<?> type, Type type1, Annotation[] antns, MediaType mt, MultivaluedMap<String, Object> mm, OutputStream out) throws IOException, WebApplicationException { 
    ImageIO.write(image, mt.getSubtype(), out); 
    } 
} 

Este MessageBodyWriter se utilizará de forma automática si el descubrimiento automático está habilitado para Jersey, de lo contrario, debe ser retornado ned por una subclase de aplicación personalizada. Ver JAX-RS Entity Providers para más información.

Una vez que esto está configurado, sólo tiene que devolver un BufferedImage de un método de recurso y será como salida según los datos del archivo de imagen:

Un par de ventajas de este enfoque:

  • Escribe en la respuesta OutputSteam en lugar de un intermediario BufferedOutputStream
  • Admite salidas png y jpg (según los tipos de medios permitidos por el método de recursos)
Cuestiones relacionadas