Как открыть URL-адрес или файл с помощью PrimeFaces commandButton [duplicate]

Альтернативы mod_rewrite

Многие базовые схемы виртуальных URL-адресов могут быть достигнуты без использования RewriteRules. Apache позволяет запускать скрипты PHP без расширения .php и с виртуальным аргументом PATH_INFO.

  1. Используйте PATH_INFO , Luke

    В настоящее время AcceptPathInfo On часто включается по умолчанию. В основном это позволяет .php и другим URL-адресам ресурсов переносить виртуальный аргумент:
    http://example.com/script.php/virtual/path
    
    Теперь этот /virtual/path отображается в PHP как $_SERVER["PATH_INFO"] , где вы можете обрабатывать любые дополнительные аргументы, как вам нравится. Это не так удобно, как разделить сегменты входного пути Apache на $1, $2, $3 и передать их как различные переменные $_GET в PHP.

  2. Включить MultiViews , чтобы скрыть расширение .php

    . Самый простой вариант - также отключить .php «Расширения файлов» в URL-адресах разрешены:
    Options +MultiViews
    
    Это означает, что Apache выбирает article.php для HTTP-запросов на /article из-за соответствующего базового имени. И это хорошо работает вместе с вышеупомянутой функцией PATH_INFO. Таким образом, вы можете просто использовать URL-адреса, такие как http://example.com/article/virtual/title. Это имеет смысл, если у вас есть традиционное веб-приложение с несколькими точками / сценариями PHP-скриптов. Обратите внимание, что MultiViews имеет другую / более широкую цель. Это повлечет за собой незначительное исполнение, потому что Apache всегда ищет другие файлы с соответствующими базовыми именами. Это фактически означает Content-Negotiation , поэтому браузеры получают лучшую альтернативу доступным ресурсам (например, article.en.php, article.fr.php, article.jp.mp4).
  3. SetType или SetHandler для сценариев без расширения .php

    . Более ориентированный подход, чтобы избежать переноса суффиксов .php в URL-адресах, - это настройка обработчика PHP для других файловых схем. Самый простой вариант - переопределить тип MIME / обработчика по умолчанию с помощью .htaccess:
    DefaultType application/x-httpd-php
    
    Таким образом, вы можете просто переименовать свой скрипт article.php только в article (без расширения), но все же обработать его как PHP-скрипт. Теперь это может иметь некоторые последствия для безопасности и производительности, поскольку теперь все файлы без расширения будут переданы через PHP. Поэтому вы можете альтернативно установить это поведение только для отдельных файлов:
    
      SetHandler application/x-httpd-php
      # or SetType 
    
    
    Это несколько зависит от настройки вашего сервера и используемого PHP SAPI. Общие альтернативы включают ForceType application/x-httpd-php или AddHandler php5-script. Снова обратите внимание, что такие настройки распространяются от одного .htaccess до подпапок. Вы всегда должны отключать выполнение сценария (SetHandler None и Options -Exec или php_flag engine off и т. Д.) Для статических ресурсов и загрузки / каталогов и т. Д.
  4. Другие схемы перезаписи Apache

    Среди его много вариантов, Apache предоставляет функции mod_alias, которые иногда работают так же хорошо, как и mod_rewrite s RewriteRules. Обратите внимание, что большинство из них должны быть настроены в разделе , но не в файлах конфигурации .htaccess для каждого каталога. ScriptAliasMatch в первую очередь предназначен для скриптов CGI, но также должен работать для PHP. Он позволяет регулярные выражения так же, как и любые RewriteRule. На самом деле это, пожалуй, самый надежный вариант для конфигурирования переднего контроллера. И простой Alias помогает с несколькими простыми схемами перезаписи. Даже простая директива ErrorDocument может использоваться, чтобы позволить скрипту PHP обрабатывать виртуальные пути. Обратите внимание, что это обходное решение kludgy, однако, запрещает все, кроме запросов GET, и наводняет error.log по определению. См. http://httpd.apache.org/docs/2.2/urlmapping.html для получения дополнительных советов.

79
задан BalusC 12 May 2015 в 16:51
поделиться

4 ответа

Введение

Вы можете получить все через ExternalContext . В JSF 1.x вы можете получить исходный HttpServletResponse объект с помощью ExternalContext#getResponse() . В JSF 2.x вы можете использовать кучу новых методов делегатов, таких как ExternalContext#getResponseOutputStream() , без необходимости захватывать HttpServletResponse из-под кожухов JSF.

В ответе вы должны установить заголовок Content-Type, чтобы клиент знал, какое приложение следует связать с предоставленным файлом. И вы должны установить заголовок Content-Length, чтобы клиент мог вычислить ход загрузки, иначе он будет неизвестен. И вы должны установить заголовок Content-Disposition на attachment, если вы хотите диалог Сохранить как , иначе клиент попытается отобразить его в строке. Наконец, просто напишите содержимое файла в выходной поток ответа.

Наиболее важной частью является вызов FacesContext#responseComplete() , чтобы сообщить JSF, что он не должен выполнять навигацию и визуализацию после того, как вы записывает файл в ответ, иначе конец ответа будет загрязнен содержимым HTML на странице или более старыми версиями JSF, вы получите IllegalStateException с сообщением типа getoutputstream() has already been called for this response, когда реализация JSF вызывает getWriter() для отображения HTML.

Общий пример JSF 2.x

public void download() throws IOException {
    FacesContext fc = FacesContext.getCurrentInstance();
    ExternalContext ec = fc.getExternalContext();

    ec.responseReset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
    ec.setResponseContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ExternalContext#getMimeType() for auto-detection based on filename.
    ec.setResponseContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
    ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.

    OutputStream output = ec.getResponseOutputStream();
    // Now you can write the InputStream of the file to the above OutputStream the usual way.
    // ...

    fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}

Общий JSF 1.x пример

public void download() throws IOException {
    FacesContext fc = FacesContext.getCurrentInstance();
    HttpServletResponse response = (HttpServletResponse) fc.getExternalContext().getResponse();

    response.reset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
    response.setContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ServletContext#getMimeType() for auto-detection based on filename.
    response.setContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
    response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.

    OutputStream output = response.getOutputStream();
    // Now you can write the InputStream of the file to the above OutputStream the usual way.
    // ...

    fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}

Пример обычного статического файла

Если вам нужно передать статический файл из локальной файловой системы на диске, замените код следующим образом:

File file = new File("/path/to/file.ext");
String fileName = file.getName();
String contentType = ec.getMimeType(fileName); // JSF 1.x: ((ServletContext) ec.getContext()).getMimeType(fileName);
int contentLength = (int) file.length();

// ...

Files.copy(file.toPath(), output);

Пример общего динамического файла

В если вам нужно передать динамически сгенерированный файл, например PDF или XLS, а затем просто предоставить output там, где используемый API ожидает OutputStream.

Например iText PDF:

String fileName = "dynamic.pdf";
String contentType = "application/pdf";

// ...

Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, output);
document.open();
// Build PDF content here.
document.close();

Например. Apache POI HSSF:

String fileName = "dynamic.xls";
String contentType = "application/vnd.ms-excel";

// ...

HSSFWorkbook workbook = new HSSFWorkbook();
// Build XLS content here.
workbook.write(output);
workbook.close();

Обратите внимание, что вы не можете установить длину содержимого здесь. Поэтому вам нужно удалить строку, чтобы задать длину содержимого ответа. Это технически не проблема, единственным недостатком является то, что у конечного пользователя будет неизвестный процесс загрузки. В случае, если это важно, вам нужно сначала записать в локальный (временный) файл, а затем предоставить его, как показано в предыдущей главе.

Отключить ajax!

Вы только необходимо убедиться, что метод действия не вызывается с помощью запроса ajax, но он вызван обычным запросом при срабатывании <h:commandLink> и <h:commandButton>. Запросы Ajax обрабатываются JavaScript, который, в свою очередь, по соображениям безопасности не имеет средств для принудительного диалога Save As с содержимым ответа ajax.

повторное использование, например PrimeFaces <p:commandXxx>, то вам нужно убедиться, что вы явно отключите ajax через атрибут ajax="false". Если вы используете ICEfaces, вам нужно вставить <f:ajax disabled="true" /> в компонент команды.

Утилита

Если вы используете служебную библиотеку JSF OmniFaces , вы можете использовать один из трех удобных методов Faces#sendFile() , принимающих либо File, либо InputStream, либо byte[], и указав, должен ли файл загружаться как вложение (true) или inline (false).

public void download() throws IOException {
    Faces.sendFile(file, true);
}

Да, этот код завершен как есть. Вам не нужно вызывать responseComplete() и так далее. Этот метод также правильно обрабатывает заголовки IE и имена файлов UTF-8. Здесь вы можете найти исходный код .

203
ответ дан BalusC 22 August 2018 в 03:08
поделиться
  • 1
    Так легко! Мне было интересно, как сделать загрузку доступной для PrimeFaces в соответствии с их витриной, потому что для p:fileDownload требуется инфраструктура InputStream, и мне не удалось преобразовать OutputStream в InputStream. Теперь ясно, что даже прослушиватель действий может изменить тип содержимого ответа, и тогда ответ будет соблюдаться как загрузка файла на стороне пользователя-агента в любом случае. Спасибо! – Lyubomyr Shaydariv 13 November 2012 в 20:45
  • 2
    Есть ли способ сделать это, используя HTTP GET вместо HTTP POST (h: commandButton и h: commandLink)? – Alfredo Osorio 13 February 2013 в 20:54
  • 3
    @Alfredo: да, используя прослушиватель preRenderView в режиме без разметки. Аналогичный вопрос для загрузки (ну, обслуживающий) JSON ответил здесь: stackoverflow.com/questions/8358006/… – BalusC 13 February 2013 в 20:55
  • 4
    Ссылка w3schools.com/media/media_mimeref.asp нарушена. Возможно, это подходит: iana.org/assignments/media-types – Zakhar 28 April 2013 в 13:59
  • 5
    @BalusC Как я могу передать несколько файлов? Я поместил это внутри для, но первый файл сохраняет & quot; downloading & quot; пока все это не будет завершено, а затем загрузится только содержимое первого файла. – StudioWorks 11 March 2015 в 13:58

здесь полный фрагмент кода http://bharatonjava.wordpress.com/2013/02/01/downloading-file-in-jsf-2/

 @ManagedBean(name = "formBean")
 @SessionScoped
 public class FormBean implements Serializable
 {
   private static final long serialVersionUID = 1L;

   /**
    * Download file.
    */
   public void downloadFile() throws IOException
   {
      File file = new File("C:\\docs\\instructions.txt");
      InputStream fis = new FileInputStream(file);
      byte[] buf = new byte[1024];
      int offset = 0;
      int numRead = 0;
      while ((offset < buf.length) && ((numRead = fis.read(buf, offset, buf.length -offset)) >= 0)) 
      {
        offset += numRead;
      }
      fis.close();
      HttpServletResponse response =
         (HttpServletResponse) FacesContext.getCurrentInstance()
        .getExternalContext().getResponse();

     response.setContentType("application/octet-stream");
     response.setHeader("Content-Disposition", "attachment;filename=instructions.txt");
     response.getOutputStream().write(buf);
     response.getOutputStream().flush();
     response.getOutputStream().close();
     FacesContext.getCurrentInstance().responseComplete();
   }
 }

Вы можете изменить логику чтения файла, если вы хотите, чтобы файл генерировался во время выполнения.

-2
ответ дан Bharat Sharma 22 August 2018 в 03:08
поделиться
  • 1
    Это позволит вам получить часть входного файла, если его размер больше 1024 байта! – hinneLinks 4 November 2015 в 09:37
public void download() throws IOException
{

    File file = new File("file.txt");

    FacesContext facesContext = FacesContext.getCurrentInstance();

    HttpServletResponse response = 
            (HttpServletResponse) facesContext.getExternalContext().getResponse();

    response.reset();
    response.setHeader("Content-Type", "application/octet-stream");
    response.setHeader("Content-Disposition", "attachment;filename=file.txt");

    OutputStream responseOutputStream = response.getOutputStream();

    InputStream fileInputStream = new FileInputStream(file);

    byte[] bytesBuffer = new byte[2048];
    int bytesRead;
    while ((bytesRead = fileInputStream.read(bytesBuffer)) > 0) 
    {
        responseOutputStream.write(bytesBuffer, 0, bytesRead);
    }

    responseOutputStream.flush();

    fileInputStream.close();
    responseOutputStream.close();

    facesContext.responseComplete();

}
3
ответ дан John Mendes 22 August 2018 в 03:08
поделиться

Это то, что сработало для меня:

public void downloadFile(String filename) throws IOException {
    final FacesContext fc = FacesContext.getCurrentInstance();
    final ExternalContext externalContext = fc.getExternalContext();

    final File file = new File(filename);

    externalContext.responseReset();
    externalContext.setResponseContentType(ContentType.APPLICATION_OCTET_STREAM.getMimeType());
    externalContext.setResponseContentLength(Long.valueOf(file.lastModified()).intValue());
    externalContext.setResponseHeader("Content-Disposition", "attachment;filename=" + file.getName());

    final HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();

    FileInputStream input = new FileInputStream(file);
    byte[] buffer = new byte[1024];
    final ServletOutputStream out = response.getOutputStream();

    while ((input.read(buffer)) != -1) {
        out.write(buffer);
    }

    out.flush();
    fc.responseComplete();
}
2
ответ дан Koray Tugay 22 August 2018 в 03:08
поделиться
  • 1
    После 2 рабочих дней это решило мою проблему с небольшими изменениями :) Большое вам спасибо. – ÖMER TAŞCI 13 August 2018 в 08:47
Другие вопросы по тегам:

Похожие вопросы: