J'utilise Spring mvc 4.3.x, java 8, Tomcat 7
CODE:
@Controller public class StreamRecordsController { @RequestMapping(value = "/streamrecords", method = RequestMethod.GET, consumes = MediaType.ALL_VALUE, produces = "application/octet-stream") @ResponseBody public ResponseEntity<StreamingResponseBody> export() throws FileNotFoundException { File file = new File("C:\\Users\\Ankur\\sample.pdf"); StreamingResponseBody responseBody = outputStream -> { Files.copy(file.toPath(), outputStream); }; return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=generic_file_name.pdf") .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(responseBody); } }
EXCEPTION:
INSTANTANÉ POSTMAN
Que me manque-t-il ici?
5 Réponses :
Il semble qu'il vous manque 2 choses:
Tout d'abord, votre code renvoie MediaType.APPLICATION_OCTET_STREAM
comme type de contenu. Ce serait bien si vous disiez à Spring que la méthode export ()
produit ce type. Vous pouvez le faire avec l'attribut produit
de @RequestMapping
.
Deuxièmement, votre navigateur ne demande pas APPLICATION_OCTET_STREAM
- vous pouvez le voir par la valeur d'en-tête Accept
. APPLICATION_OCTET_STREAM
correspond à application / octet-stream
- votre demande du navigateur devra l'inclure dans la valeur d'en-tête Accept
, afin que Spring puisse reconnaissez, quelle méthode doit être appelée dans votre contrôleur.
Modifier: après avoir résolu le problème, jetez un œil sur l'annotation @RestController
qui peut être utilisée à la place de @Controller
- vous n'aurez pas à ajouter Annotation @ResponseBody
, car elle sera incluse par défaut. Regardez également @GetMapping
qui est une superposition pour l'annotation @RequestMapping
pour les méthodes HTTP GET.
Merci beaucoup pour votre temps et vos commentaires. Je vous tiendrai au courant sous peu.
La sortie est toujours la même, veuillez trouver le code mis à jour ci-dessus.
406 Not Acceptable
La ressource identifiée par la requête est uniquement capable de générer des entités de réponse dont les caractéristiques de contenu ne sont pas acceptables selon les en-têtes d'acceptation envoyés dans la requête.
Essayez d'utiliser l'annotation Produces dans votre méthode de contrôleur:
@Produces({MediaType.APPLICATION_JSON})
La sortie est toujours la même, veuillez trouver le code mis à jour ci-dessus.
Ce qui vous arrive est peut-être un bogue Spring MVC. Avec les navigateurs et cela varie également selon les navigateurs - Accepter l'en-tête dans la demande est obligatoire avec des valeurs correctes. Vous pouvez avoir un comportement différent dans un navigateur différent et votre code peut fonctionner dans une interface utilisateur swagger.
Selon votre capture d'écran, votre en-tête Accept ne correspond pas au type de réponse, il doit être * / *
car votre code est spécial dans le sens où vous ne prenez aucune entrée qui soit un scénario rare dans le monde REST.
Je suggère donc d'ajouter des consommations dans votre cartographie comme ci-dessous et de voir si cela fonctionne,
@RequestMapping (value = "/ streamrecords", method = RequestMethod.GET, consumes = MediaType.ALL_VALUE)
et assurez-vous que l'en-tête Accept dans la requête porte la valeur - * / *
.
Merci Sabir, Je vais vous mettre à jour sous peu à ce sujet .... Permettez-moi de vérifier les changements ci-dessus.
La sortie est toujours la même, veuillez trouver le code mis à jour ci-dessus.
Quel est votre client Rest? Du code JavaScript / Angular, etc. ou autre chose? Essayez de définir la valeur d'en-tête Accept
sur * / *
dans votre client, car elle doit être définie dans l'en-tête de la demande.
My Rest Client est un facteur et un navigateur Web. Si je me souviens bien, j'ai essayé * / *
... les choses n'ont pas fonctionné
Swagger lance ceci - curl -X GET "http: // localhost: 7001 / streamrecords" -H "accept: * / *"
et cela fonctionne. Votre navigateur a-t-il montré que la valeur de l'en-tête Accepter était * / *
après que votre code ait changé ou que la valeur soit restée la même que celle indiquée dans la capture d'écran initiale?
Swagger me donne la possibilité de télécharger le fichier et c'est la même chose dans Postman si je définis Accept = * / *
, c'est-à-dire que le statut HTTP est de 200 pour les deux clients. Si la valeur d'en-tête Accept reste la même même après avoir explicitement défini cette valeur d'en-tête, vous devez publier ce code et les captures d'écran du facteur.
Avez-vous utilisé Spring Boot? Si oui, je n'utilise pas Spring Boot ici. C'est du pur Spring MVC 4.x
Oui, je suis sur la version Spring Boot - 1.5.2.RELEASE
.
Merci pour la réponse, pouvez-vous me dire comment pouvons-nous y parvenir sans utiliser Spring Boot.
Dans ce cas, il peut s'agir de bibliothèques json manquantes / incompatibles - ce n'est que le point de départ. Je suggère de télécharger votre code complet quelque part et de fournir un lien.
Cette erreur indique que le client (par exemple, le navigateur) s'attend à ce qu'une sorte de contenu X (via l'en-tête Accept par exemple, application / json) soit envoyé par le serveur, mais le serveur ne fournit pas ce contenu sur un point final donné (par exemple, ne produit que du XML) .
Dans votre cas, vous "acceptez" plusieurs formats, mais aucun d'entre eux n'est application / octet-stream
et c'est ce que vous avez déclaré que le serveur renverra dans @RequestMapping ( produit une annotation = "application / octet-stream")
. Incluez-le dans votre navigateur d'en-tête Accept
.
Merci pour la réponse, pouvez-vous me dire comment inclure l'en-tête Accepter dans le navigateur Chrome?
Enfin, j'ai pu régler ce problème, ce qui m'aide maintenant à diffuser d'énormes données du backend vers le frontend en utilisant Spring 4.3.x comme mentionné dans mon article. Voici les points pour vous guider pour exécuter le programme avec succès.
La procédure ci-dessous est si efficace que vous pouvez même paginer d'énormes données à l'arrière (peut être mise en veille prolongée, pilote Mongo-java, pilote java cassandra, etc.) et continuez à diffuser les données à moins que votre opération de base de données ne soit terminée. Dans certains domaines comme la fabrication, l'assurance, la logistique, etc., vous avez besoin d'un tel utilitaire où l'utilisateur final attend d'énormes données silencieuses du serveur sous la forme de CSV, JSON, etc. pour analyser les données brutes.
Ajoutez une autre annotation @EnableWebMvc
au-dessus de votre classe de contrôleur.
Lorsque vous ajoutez l'annotation ci-dessus, le code se cassera lors de l'exécution, vous pouvez voir dans catalina.log
, cette erreur: java.lang.NoClassDefFoundError: com / plus rapidexml / jackson / core / util / DefaultIndenter
Pour résoudre ce problème, vous devrez ajouter ci-dessous la dépendance jar dans votre pom.xml
package com.emg.server.controller.rest; import java.io.File; import java.nio.file.Files; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; @Controller @EnableWebMvc public class StreamRecordsController { @RequestMapping(value = "/streamrecords", method = RequestMethod.GET, produces = "application/json; charset=UTF-8") @ResponseBody public ResponseEntity<StreamingResponseBody> export() { File file = new File("C:\\Users\\Ankur\\sample.pdf"); StreamingResponseBody responseBody = outputStream -> { Files.copy(file.toPath(), outputStream); }; return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=generic_file_name.pdf") .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(responseBody); } }
Maintenant, ajoutez
dans web.xml
sous
tag comme exemple ci-dessous,
package com.emg.server.controller.rest; import java.io.IOException; import java.io.OutputStream; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; @Controller @EnableWebMvc public class StreamRecordsController { @RequestMapping(value = "/streamrecords") @ResponseBody public StreamingResponseBody export() { return new StreamingResponseBody() { @Override public void writeTo (OutputStream out) throws IOException { for (int i = 0; i < 1000; i++) { out.write((Integer.toString(i) + " - ") .getBytes()); out.flush(); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } } }; } }
Vous trouverez ci-dessous le code pour le téléchargement de FILE Streaming et la prise en charge du flux de données.
CODE DE FLUX DE DONNÉES:
<servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet>
Il dit que le client (par exemple le navigateur) s'attend à ce qu'une sorte de contenu X (via l'en-tête
Accept
, par exempleapplication / json
) soit envoyé par le serveur, mais le serveur ne fournit pas un tel contenu sur un point final donné (par exemple, ne produit que du XML).Essayez sans aucun en-tête
Accept
dans le facteur.@Antoniossss Vous êtes un génie. Je vous remercie