Afficher l'image dynamique à partir de la base de données avec P: graphicImage et StreamedContent

j'essaie d'Afficher des octets d'image qui est enregistré dans la base de données comme un StreamedContent dans le <p:graphicImage> comme suit:

<p:graphicImage  value="#{item.imageF}" width="50"  id="grpImage" height="80"/>
private StreamedContent content; // getter and setter

public StreamedContent getImageF() {

    if (student.getImage() != null) {
        InputStream is = new ByteArrayInputStream(student.getImage());
        System.out.println("Byte :"+student.getImage());
        content = new DefaultStreamedContent(is, "", student.getStuID());
        System.out.println("ddd ------------------------------- " + content);
        return content;
    }

    return content;
}

renvoie une image vierge. Comment cela est-il causé et comment puis-je le résoudre?

le stdout imprime ce qui suit:

INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@b0887b
INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1d06a92
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@39a60
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@8c3daa
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1dbe05b
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@66a266
INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1293976
INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@17b7399
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1e245a5
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@4a7153
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1561bfd
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@47a8c2
42
demandé sur BalusC 2011-11-21 08:36:38

4 réponses

le <p:graphicImage> nécessite une méthode getter spéciale. Il sera notamment invoqué deux fois par image générée, chacune dans une requête HTTP complètement différente.

la première requête HTTP, qui a demandé le résultat HTML d'une page JSF, invoquera le getter pour la première fois afin de générer L'élément HTML <img> avec la bonne URL unique et générée automatiquement dans l'attribut src qui contient des informations sur lequel bean et getter exactement devrait être invoquée chaque fois que le webbrowser est sur le point de demander l'image. Notez que le getter ne à ce moment pas besoin de retourner le contenu de l'image. Il ne serait pas utilisé de quelque manière que ce soit car ce N'est pas comme ça que le HTML fonctionne (les images ne sont pas "inlined" dans la sortie HTML, mais elles sont plutôt demandées séparément).

une fois que le navigateur récupère le résultat HTML comme réponse HTTP, il analyse le source HTML afin de présenter le résultat visuellement à l'utilisateur final. Une fois que le webbrowser rencontre un élément <img> lors de l'analyse de la source HTML, il envoie une toute nouvelle requête HTTP sur L'URL comme spécifié dans son attribut src afin de télécharger le contenu de cette image et de l'intégrer dans la présentation visuelle. Cela va invoquer la méthode getter pour la deuxième fois qui à son tour devrait retourner le réel le contenu de l'image.

dans votre particulier cas PrimeFaces était apparemment soit incapable d'identifier et d'invoquer le getter afin de récupérer le contenu image réel, ou le getter n'a pas retourné le contenu image attendu. L'utilisation de #{item} nom de la variable et la beaucoup d'appels dans le journal suggère que vous l'utilisiez dans un <ui:repeat> ou un <h:dataTable> . Il est très probable que le support soit une requête scopée et que le datamodel ne soit pas correctement préservé pendant la requête pour l'image et JSF ne sera pas en mesure d'invoquer de la lecture au cours de l'itération droit à la ronde. Une vue scoped bean ne fonctionnerait pas non plus car L'état de vue JSF n'est disponible nulle part lorsque le navigateur demande réellement l'image.


" lors de requêtes HTTP ultérieures. Il serait tout à fait logique d'utiliser une application séparée scoped géré bean pour ce qui n'a pas d'état. De plus, un InputStream ne peut être lu qu'une seule fois, et non plusieurs.

en d'autres termes: ne jamais déclarer StreamedContent ni aucune InputStream ou même UploadedFile comme une propriété de haricot; seulement le créer brand-new dans le getter d'un haricot apatride @ApplicationScoped quand le webbrowser demande réellement le contenu de l'image .

E. G.

<p:dataTable value="#{bean.students}" var="student">
    <p:column>
        <p:graphicImage value="#{studentImages.image}">
            <f:param name="studentId" value="#{student.id}" />
        </p:graphicImage>
    </p:column>
</p:dataTable>

où le StudentImages peut ressembler à ceci:

@Named // Or @ManagedBean
@ApplicationScoped
public class StudentImages {

    @EJB
    private StudentService service;

    public StreamedContent getImage() throws IOException {
        FacesContext context = FacesContext.getCurrentInstance();

        if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
            // So, we're rendering the HTML. Return a stub StreamedContent so that it will generate right URL.
            return new DefaultStreamedContent();
        }
        else {
            // So, browser is requesting the image. Return a real StreamedContent with the image bytes.
            String studentId = context.getExternalContext().getRequestParameterMap().get("studentId");
            Student student = studentService.find(Long.valueOf(studentId));
            return new DefaultStreamedContent(new ByteArrayInputStream(student.getImage()));
        }
    }

}

veuillez noter qu'il s'agit d'un cas très particulier dans lequel la logique de fonctionnement dans une méthode getter est tout à fait légitime, compte tenu de la façon dont le <p:graphicImage> fonctionne sous les couvertures. Invoquer la logique commerciale dans getters est généralement mal vu, Voir aussi pourquoi JSF appelle getters plusieurs fois . Ne pas utiliser ce cas spécial comme excuse pour d'autres cas standard (non-spécial). Veuillez également noter que vous ne pouvez pas utiliser la fonctionnalité EL 2.2 Pour passer des arguments de méthode comme #{studentImages.image(student.id)} parce que cet argument ne finira pas dans L'URL de l'image. Ainsi, vous devez vraiment les passer comme <f:param> .


si vous utilisez OmniFaces 2.0 ou plus récent , alors envisager d'utiliser son <o:graphicImage> à la place qui peut être utilisé plus intuitivement,avec une application scoped getter méthode déléguant directement à la méthode de service et le soutien EL 2.2 arguments de méthode.

ainsi:

<p:dataTable value="#{bean.students}" var="student">
    <p:column>
        <o:graphicImage value="#{studentImages.getImage(student.id)}" />
    </p:column>
</p:dataTable>

avec

@Named // Or @ManagedBean
@ApplicationScoped
public class StudentImages {

    @EJB
    private StudentService service;

    public byte[] getImage(Long studentId) {
        return studentService.find(studentId).getImage();
    }

}

Voir aussi le blog sur le sujet.

87
répondu BalusC 2017-05-23 12:10:02

essayez d'inclure un type mime. Dans votre exemple posté, vous l'avez comme "". L'image vierge peut être parce qu'elle ne reconnaît pas le flux comme un fichier image depuis que vous avez fait ce champ une chaîne vide. Ajoutez donc un type mime d'image / png ou image / jpg et voyez si cela fonctionne:

String mimeType = "image/jpg";
StreamedContent file = new DefaultStreamedContent(bytes, mimeType, filename);  
5
répondu rcheuk 2012-09-26 22:24:27

il y a quelques possibilités ici (et s'il vous plaît poster toute la classe si ce n'est pas cela).

1) vous n'initialisez pas l'image correctement 2) Votre flux est vide donc vous ne recevez rien

je suppose étudiant.getImage () a une signature de byte[] alors assurez-vous d'abord que ces données sont réellement intactes et représentent une image. Deuxièmement--vous ne spécifiez pas un type de contenu qui devrait être "image/jpg" ou ce que vous utilisez.

voici un code boilerplate pour le vérifier, J'utilise Primefaces 2 pour ça.

/** 'test' package with 'test/test.png' on the path */
@RequestScoped
@ManagedBean(name="imageBean")
public class ImageBean
{
    private DefaultStreamedContent content;

    public StreamedContent getContent()
    {
        if(content == null)
        {
            /* use your database call here */
            BufferedInputStream in = new BufferedInputStream(ImageBean.class.getClassLoader().getResourceAsStream("test/test.png"));
            ByteArrayOutputStream out = new ByteArrayOutputStream();

            int val = -1;
            /* this is a simple test method to double check values from the stream */
            try
            {
                while((val = in.read()) != -1)
                    out.write(val);
            }
            catch(IOException e)
            {
                e.printStackTrace();
            }

            byte[] bytes = out.toByteArray();
            System.out.println("Bytes -> " + bytes.length);
            content = new DefaultStreamedContent(new ByteArrayInputStream(bytes), "image/png", "test.png");
        }

        return content;
    }
}

et un peu de marge...

<html 
    xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:p="http://primefaces.prime.com.tr/ui"
>

    <h:head>

    </h:head>

    <h:body>
        <p:graphicImage value="#{imageBean.content}" />
    </h:body>
</html>

si ce code fonctionne, vous êtes configuré correctement. Malgré le fait qu'il s'agit d'un code poubelle pour les flux (ne l'utilisez pas dans la production) il devrait vous donner un point de dépannage. Je pense que vous avez peut-être quelque chose qui se passe dans votre JPA ou autre base de données le cadre où vous êtes octet [] est vide ou il est mal formaté. Alternativement, vous pourriez juste avoir un problème de type de contenu.

enfin, je clonerais les données de la fève pour que l'étudiant.getImage() ne seraient copiés dans un nouveau tableau et ensuite utilisé. De cette façon, si vous avez quelque chose d'inconnu en cours (quelque chose d'autre qui déplace l'objet ou qui change le byte[] vous ne jouez pas avec vos flux.

Faire quelque chose comme:

byte[] data = new byte[student.getImage().length]
for(int i = 0; i < data.length; i++)
  data[i] = student.getImage()[i];

de sorte que votre haricot a une copie (ou tableaux.copier()--tout ce qui flotte votre bateau). Je ne peux pas souligner assez comment quelque chose simple comme ceci / type de contenu est généralement ce qui ne va pas. Bonne chance avec elle.

4
répondu Daniel B. Chapman 2011-11-27 17:48:46

La réponse de BalusC est (comme d'habitude) la bonne.

mais gardez une chose à l'esprit (comme il l'a déjà dit). La requête finale est faite à partir du navigateur pour obtenir L'URL à partir de la balise <img> construite. Cela ne se fait pas dans un "contexte jsf".

donc si vous essayez par exemple d'accéder à la phaseId (logging ou n'importe quelle raison)

context.getCurrentPhaseId().getName()

il en résultera un NullPointerException et le d'une manière ou d'une autre, le message d'erreur que vous obtiendrez est:

org.primefaces.application.resource.StreamedContentHandler () - Error in streaming dynamic resource. Error reading 'image' on type a.b.SomeBean

Il m'a fallu un certain temps pour comprendre quel était le problème.

2
répondu morecore 2017-05-23 12:34:15