2012-02-09 8 views
38

Mam serwer WWW działający z zasobami REST Jersey i zastanawiam się, jak uzyskać odniesienie do obrazu/png dla przeglądarki img tag; po przesłaniu formularza lub uzyskaniu odpowiedzi Ajax. Kod przetwarzania grafiki do dodawania grafiki działa, po prostu trzeba go jakoś zwrócić.Jak przywrócić obraz PNG z metody usługi REST firmy Jersey do przeglądarki

Kod:

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

} 

Cheers

+0

Co próbujesz osiągnąć? Nie możesz tego osiągnąć, wysyłając identyfikator URI z lokalizacją obrazu? – Perception

+0

Chcę, aby użytkownik wyświetlił podgląd wybranej grafiki na zdjęciu przed złożeniem zamówienia. Widzę teraz, że nie można tego zrobić za pomocą posta AJAX, będzie musiał zażądać stron internetowych, jak powiedziałeś, wskazując na przetworzony obraz. – gorn

Odpowiedz

79

nie jestem przekonany, to dobry pomysł, aby powrócić danych obrazowych w usłudze REST. Łączy pamięć serwera aplikacji i przepustowość we-wy. Znacznie lepiej jest przekazać to zadanie na właściwy serwer internetowy zoptymalizowany pod kątem tego rodzaju transferu. Można to osiągnąć, wysyłając przekierowanie do zasobu obrazu (jako odpowiedź HTTP 302 z identyfikatorem URI obrazu). Zakłada to oczywiście, że twoje zdjęcia są ułożone jako treść internetowa.

Mimo, że jeśli zdecydujesz naprawdę trzeba przenieść dane obrazu z usługi internetowej można to zrobić za pomocą następującego kodu (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(); 
} 

Dodaj do obsługi wyjątków, itp itd

+0

Dziękujemy! To jeden sposób, aby to zrobić. – gorn

+0

Została zakończona aplikacją serwera PHP, która wykorzystała cURL do pobrania obrazów z tej usługi internetowej RESTful Java i wskazała je w tagu obrazu HTML. – gorn

+0

@Gorn powinieneś napisać swoje rozwiązanie w swojej odpowiedzi jako edycję – kommradHomer

11

zbudowałem ogólny sposób, że z następujących cech:

  • zawracania „nie modyfikowane”, jeśli plik nie został zmodyfikowany lokalnie, o Status.NOT_MODIFIED jest wysyłany do rozmówcy. Używa Apache Commons Lang
  • pomocą obiektu strumienia pliku zamiast czytać sam plik

Oto kod:

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

Należy pamiętać, że przełączanie Locale nie wydaje się być bezpieczny wątku. Myślę, że lepiej zmienić ustawienia regionalne na całym świecie. Nie jestem pewien co do efektów ubocznych, chociaż ...

+3

Możesz usunąć wiele ostatnio zmodyfikowanych elementów logicznych, używając parametru Jersey Request.evaluatePreconditions (...), ponieważ będzie on obsługiwał analizowanie i sprawdzanie dat oraz etagów, jeśli je obsługujesz. – bramp

6

w odniesieniu do odpowiedzi z @Perception, to prawda, że ​​jest bardzo zajmująca pamięć podczas pracy z tablicami bajtowymi, ale można również po prostu zapisać z powrotem do strumienia wyjściowego

@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

Jeśli masz kilka metod Zasób obrazu, to warto tworząc MessageBodyWriter wyprowadzania 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); 
    } 
} 

Ten MessageBodyWriter zostaną wykorzystane automatycznie jeśli auto-discovery jest włączona do Jersey, w przeciwnym razie musi zostać zwrócony ned przez niestandardową podklasę aplikacji. Aby uzyskać więcej informacji, patrz JAX-RS Entity Providers.

Gdy to skonfigurować, po prostu wrócić BufferedImage z metody zasobów i będzie mieć moc danych pliku obrazu:

Kilka zalet tego podejścia:

  • Zapisuje do odpowiedzi OutputSteam, a nie pośrednika BufferedOutputStream
  • Obsługuje zarówno dane wyjściowe png, jak i jpg (w zależności od typów mediów dozwolonych przez metodę zasobów)
Powiązane problemy