2015-11-09 6 views
8

Idee: Ich habe eine Spring Web MVC Aktion, die einen oder beide dieser taks erreichen sollte:herunterladen oder umleiten mit Fehlermeldung an einen anderen Controller-Aktion in Spring Web MVC

  • Herunterladen einer Datei von einem entfernten Server und schreibe den Eingangsstrom in die Antwort Ausgangsstrom
  • Oder fangen Sie die Ausnahme des Downloads, stellen Sie eine von mehreren Fehlermeldungen und umleiten auf die/Adressen Seite. Die Adresse Seite wird der Fehler angezeigt

Problem: Der Frühling ist nicht in der Lage, eine Datei herunterzuladen und im Falle eines Problems zu umleiten - irgendwie Attribute blinken, weil nicht funktionieren die in der Umleitung verloren gehen:

@ResponseBody 
@RequestMapping(value = "/download/{fileaddress}", method = RequestMethod.GET) 
public void download(HttpServletRequest request, HttpServletResponse response, @PathVariable(value = "fileaddress") String fileaddress) throws Exception 
{ 
    if(fileaddress != null && fileaddress.length() > 0) 
    { 
     try 
     { 
      // Get the remove file based on the fileaddress 
      RemoteFile remotefile = new RemoteFile(fileaddress); 

      // Set the input stream 
      InputStream inputstream = remotefile.getInputStream(); 

      // Write the input stream to the output stream or throw an exception 
      Utils.writeTo(inputstream, response.getOutputStream()); 
     } 
     catch(MyExceptionA) 
     { 
      // TODO: Define error message a and pass it to /addresses 
      // PROBLEM: Flash attributes that contain all critical error information don't work 
      response.sendRedirect(request.getContextPath() + "/addresses"); 
     } 
     catch(MyExceptionB) 
     { 
      // TODO: Add another error message and redirect 
      response.sendRedirect(request.getContextPath() + "/addresses"); 
     } 
     catch(MyExceptionC) 
     { 
      // TODO: Add another error message and redirect 
      response.sendRedirect(request.getContextPath() + "/addresses"); 
     } 
     catch(MyExceptionN) 
     { 
      // TODO: Add another error message and redirect 
      response.sendRedirect(request.getContextPath() + "/addresses"); 
     } 
    } 
    else 
    { 
     // TODO: Add error message 
     response.sendRedirect(request.getContextPath() + "/addresses"); 
    } 
} 

JSP-Seite von/Adressen:

<%@ page pageEncoding="UTF-8" %> 
<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %> 
<%@ taglib prefix="core" uri="http://java.sun.com/jsp/jstl/core" %> 

<tags:index> 
    <jsp:attribute name="content"> 
     <core:if test="${not empty error}"> 
      <div class="alert alert-danger"> 
       <p>${error}</p> 
      </div> 
     </core:if> 
     <p>Page under construction!</p> 
    </jsp:attribute> 
</tags:index> 

Frage: Wie ich in der Lage bin die Fehlermeldung angezeigt werden (Einfache Zeichenfolge zum Beispiel) in der/Adressen-Site? Das Arbeiten mit verschiedenen URL-Parametern (error = errora, error = errorb ...) ist ein großer Schmerz, wenn es mehrere Fehlertypen gibt und die Fehlermeldung als GET-Parameter unprofessionell übergibt und die Wurzel von Encodierungsproblemen ist.

Antwort

7

Was Sie brauchen, ist die RedirectAttributes eine Spezialisierung der Model die Controller verwenden können, um Attribute für ein Redirectszenario auszuwählen. Also für ein funktionierendes Beispiel eines code:

@ResponseBody 
@RequestMapping(value = "/download/{fileaddress}", method = RequestMethod.GET) 
public Object download(@PathVariable(value = "fileaddress") String fileaddress, RedirectAttributes redirectAttrs) throws Exception { 
    if(StringUtils.hasText(fileaddress)){ 
     try{ 
      // Get the remove file based on the fileaddress 
      RemoteFile remotefile = new RemoteFile(fileaddress); 

      // Set the input stream 
      InputStream inputstream = remotefile.getInputStream(); 
      // asume that it was a PDF file 
      HttpHeaders responseHeaders = new HttpHeaders(); 
      InputStreamResource inputStreamResource = new InputStreamResource(inputStream); 
      responseHeaders.setContentLength(contentLengthOfStream); 
      responseHeaders.setContentType(MediaType.valueOf("application/pdf")); 
      return new ResponseEntity<InputStreamResource> (inputStreamResource, 
             responseHeaders, 
             HttpStatus.OK); 
     } catch (MyExceptionA | MyExceptionB | MyExceptionC | MyExceptionD ex) { 
      redirectAttrs.addFlashAttribute("error", ex.getMessage()); 
     }   
    } else { 
     redirectAttrs.addFlashAttribute("error", "File name is required"); 
    } 
    return "redirect:/addresses"; 
} 
+0

Hier könnte ein Problem sein, da IOUtils.toByteArray (inputstream) alles von der Datei in den Speicher liest. Wenn eine Datei zu groß ist, funktioniert diese Vorgehensweise nicht. – Kuvaldis

+0

Ich stimme zu, aber die Frage ist über die '' RedirectAttributes'' nicht zum Streamen von Dateien: D – Babl

+0

Ich habe das Codebeispiel aktualisiert, um Ihre In-Memory-Problem zu beheben :) – Babl

1

ich nicht erklären kann, warum es nicht eine Datei vom Server, sondern kommen auf Ihre zweite Abfrage heruntergeladen haben, können Sie auf zwei Arten umleiten entweder Ihre Methode Rückgabetyp als Modeland nehmen und wie diese

ModelAndView mav = new ModelAndView("write here your jsp page name even is will work for remote server"); 
     try { 
      mav.addObject("some key","here you can write any message that will you can show in your jsp page.."); 
tun

dann zurück mav. so können Sie umleiten, wie viele Seiten Sie möchten, wenn Bedingung.

und zweiten Ansatz können Sie Ihre Methode Rückgabetyp String nehmen und Sie können direkt zu Ihrem erforderlichen JSP umleiten.

+0

Sehen Sie sich einfach den Download als Eingabestream an, in dem ein Problem auftreten kann (Trennen während des Schreibvorgangs usw.) Das Problem ist: Sie können nicht in den Ausgabestream schreiben und später Redirect im Falle eines Problems - so denke ich Antwort ist nicht wirklich möglich – swaechter

+0

Auch können Sie nicht mehrmals aus dem Eingangsstrom lesen – swaechter

1

Update: ich die Frage nachgedacht haben Situationen zu, wo RedirectAttributes nicht verfügbar sind, da sonst die Lösung ziemlich offensichtlich ist (verwenden RedirectAttributes).

Orignal Antwort:

ich den folgenden Code bin mit Nachrichten an die Flash-Karte zu binden, in Situationen, in denen Feder nicht RedirectAttributes unterstützt (zB in ExceptionHandler Methoden):

public static Feedback getInstance(HttpServletRequest request, HttpServletResponse response) throws IllegalArgumentException { 
    FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request); 
    Object o = flashMap.get(KEY); 
    if (o != null) { 
     if (o instanceof Feedback) { 
      return Feedback.class.cast(o); 
     } else { 
      throw new IllegalArgumentException(...); 
     } 
    } else { 
     FeedbackContainer feedbackContainer = new FeedbackContainer(); 
     flashMap.put(KEY, feedbackContainer); 
     FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request); 
     flashMapManager.saveOutputFlashMap(flashMap, request, response); 
     return feedbackContainer; 
    } 

Wobei /FeedbackContainer ein Container für Nachrichten ist, auf die dann in JPS über JSON-Serialisierung zugegriffen wird. In Ihrem Fall können Sie eine bloße String mit der Taste „Fehler“ verwenden und es in der JSP direkt zugreifen:

void storeErrorMsg(HttpServletRequest request, HttpServletResponse response, String message) { 
    FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request); 
    flashMap.put("error", message); 
    FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request); 
    flashMapManager.saveOutputFlashMap(flashMap, request, response); 
} 

Der Hauptgrund für meine eigenen Nachricht Behälter verwendet, ist die Möglichkeit, mehrere Nachrichten mit verschiedenen Ebenen zu haben und zusätzlich getInstance Methoden für RedirectAttributes, Model oder ModelMap, so muss ich nicht auf doppelte Feedback-Bindungen und/oder die verschiedenen Bindungscode kümmern.

+0

Vielen Dank für die Idee, aber können Sie die Frage so bearbeiten, dass es ein vollständiges Arbeitsbeispiel bietet? Zum Beispiel, wie ist KEY definiert? – swaechter

+0

KEY ist eine Konstante, die die Zeichenfolge zum Speichern meiner Feedback-Instanz enthält, z. "Feedback", so kann ich in der JSP auf alle Nachrichten über '$ {feedback.all}' zugreifen (wenn 'Feedback' eine Methode 'getAll()' definiert). In Ihrem Fall ist es einfach "Fehler", aber Sie können einen beliebigen Wert wählen. – Robin

0

Ich würde den folgenden Ansatz vorschlagen: Da Sie eine Remote-Datei als Eingabestrom haben Sie es nicht von der Stelle lesen, wo eine Ausnahme aufgetreten. Daher können Sie die Fehlerwahrscheinlichkeit verringern, indem Sie zuerst die Datei in einen temporären Ordner auf dem lokalen Host herunterladen.

Hier, wenn ein Fehler vorliegt, können Sie entweder umleiten, wie Sie es vorgeschlagen haben, oder einfach einen weiteren Versuch machen und nach mehreren fehlgeschlagenen Versuchen umleiten. Auf diese Weise schreiben Sie nichts in den Stream, bis Sie sicher sind, dass die Datei erfolgreich heruntergeladen wurde.

Sobald die Datei auf den Server heruntergeladen wurde, können Sie sie an den Client übergeben. Zum Beispiel können Sie Bytes lesen und die aktuelle Position speichern. Angenommen, es gibt eine Ausnahme während des Schreibens in den Stream. Sie können das Schreiben von fehlgeschlagenen Punktlesungen aus der Mitte wiederherstellen.

Ein kleines Beispiel

... 
    final String localFileName = saveFileLocally(fileaddress) 
    if (localFileName == null) { 
     response.sendRedirect(request.getContextPath() + "/addresses"); 
    } else { 
     safeWrite(new BufferedInputStream(new FileInputStream(new File(localFileName))), response.getOutputStream()); 
    } 
... 

safeWrite sollte aus der Datei mittleren Ausnahmebehandlung lokale Datei unterstützen lesen mit dem Lesen. Es sollte Standard-Java API für solche Operationen geben.

+0

Aus Sicherheitsgründen und die doppelte Download-Zeit ist dies keine echte Option;) – swaechter

Verwandte Themen