2012-02-09 13 views
38

Ho un server Web in esecuzione con le risorse Jersey REST e mi chiedo come ottenere un riferimento immagine/png per il tag img del browser; dopo aver inviato un modulo o ottenuto una risposta Ajax. Il codice di elaborazione dell'immagine per l'aggiunta di elementi grafici funziona, è sufficiente restituirlo in qualche modo.Come restituire un'immagine PNG dal metodo di servizio Jersey REST al browser

Codice:

@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. .. ? 

} 

Acclamazioni

+0

Cosa stai cercando di realizzare? Non puoi raggiungere questo inviando un URI con la posizione dell'immagine? – Perception

+0

Desidero che l'utente visualizzi l'anteprima della grafica selezionata sulla foto prima di effettuare un ordine. Ora vedo che questo non può essere fatto con il post AJAX, avremo bisogno di richiedere le pagine web come hai detto indicando l'immagine elaborata. – gorn

risposta

79

io non sono convinto è una buona idea per restituire i dati di immagine in un servizio REST. Lega la memoria del server delle applicazioni e la larghezza di banda dell'IO. È molto meglio delegare tale attività a un server Web adeguato ottimizzato per questo tipo di trasferimento. È possibile farlo inviando un reindirizzamento alla risorsa immagine (come risposta HTTP 302 con l'URI dell'immagine). Ciò presuppone naturalmente che le immagini siano disposte come contenuti web.

Detto questo, se si decide realmente bisogno di trasferire i dati delle immagini da un servizio Web è possibile farlo con il seguente codice (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(); 
} 

Aggiungi nella gestione delle eccezioni, ecc ecc

+0

Grazie! Questo è un modo per farlo. – gorn

+0

È terminato con un'applicazione server PHP che utilizzava cURL per ottenere immagini da questo servizio Web Java RESTful e le indicava nel tag immagine HTML. – gorn

+0

@gorn dovresti scrivere la tua soluzione nella tua risposta come una modifica – kommradHomer

11

ho costruito un metodo generale per quella con le seguenti caratteristiche:

  • restituendo "non modificato" se il file non è stato modificato a livello locale, uno Status.NOT_MODIFIED viene inviato al chiamante. Utilizza Apache Commons Lang
  • utilizzando un oggetto flusso di file invece di leggere il file stesso

Ecco il codice:

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 
}; 

Si noti che la commutazione Locale non sembra essere thread-safe. Penso che sia meglio cambiare le impostazioni internazionali a livello globale. Tuttavia, non sono sicuro degli effetti collaterali ...

+3

È possibile rimuovere molta della logica modificata per ultimo utilizzando la funzione Request.evaluatePreconditions (...) di Jersey, in quanto gestirà l'analisi e il controllo delle date e gli etags se vengono supportati. – bramp

6

in relazione alla risposta di @Perception, è molto dispendioso in termini di memoria quando si lavora con gli array di byte, ma è anche possibile scrivere nuovamente nell'outstream

@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

Se si dispone di un certo numero di metodi di risorse immagine, vale la pena creare un MessageBodyWriter per l'uscita del 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); 
    } 
} 

Questa MessageBodyWriter verrà utilizzato automaticamente se il rilevamento automatico è abilitato per Jersey, in caso contrario, deve essere restituito creata da una sottoclasse dell'applicazione personalizzata. Vedi JAX-RS Entity Providers per maggiori informazioni.

Una volta che questo è impostato, semplicemente restituire un BufferedImage da un metodo di risorse e sarà emesso come dati di file immagine:

un paio di vantaggi di questo approccio:

  • Scrive sulla risposta OutputSteam anziché su un intermediario BufferedOutputStream
  • Supporta sia l'output png che jpg (a seconda dei tipi di supporto consentiti dal metodo risorsa)
Problemi correlati