Как сохранить файл на сервере (веб-контейнере) через веб-приложение Java EE?

Я разработал веб-приложение Java EE. Это приложение позволяет пользователю загружать файл с помощью браузера. После того, как пользователь загрузил свой файл, это приложение сначала сохраняет загруженный файл на сервере (на котором он запущен), а затем обрабатывает его.

В настоящее время я храню файл на сервере следующим образом:

try {
    // formFile represents the uploaded file
    FormFile formFile = programForm.getTheFile();
    String path = getServlet().getServletContext().getRealPath("") + "/"
        + formFile.getFileName();
    System.out.println(path);
    file = new File(path);
    outputStream = new FileOutputStream(file);
    outputStream.write(formFile.getFileData());
}

где formFile представляет загруженный файл.

Теперь проблема в том, что он работает на некоторых серверах, но на некоторых серверах getServlet().getServletContext().getRealPath("") возвращает null, поэтому последний путь, который я получаю, null/filename, и файл не сохраняется на сервер.

Когда я проверил API для ServletContext.getRealPath(), я обнаружил следующее:

public java.lang.String getRealPath(java.lang.String path)

Возвращает строку, содержащую реальный путь для данного виртуального пути. Например, путь "/index.html" возвращает абсолютный путь к файлу на файловой системе сервера, будет обслуживаться запросом для "http://host/contextPath/index.html", где contextPath - это путь контекста этого ServletContext.

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

Итак, есть ли другой способ, с помощью которого я могу хранить файлы на тех серверах, которые возвращают null для getServlet().getServletContext().getRealPath("")

Ответы

Ответ 1

Запись в файловую систему из контейнера Java EE на самом деле не рекомендуется, особенно если вам нужно обработать записанные данные:

  • это не транзакционный
  • это вредит переносимости (что, если вы находитесь в кластерной среде)
  • требуется установка внешних параметров для целевого местоположения.

Если это опция, я бы сохранил файлы в базе данных или использовал репозиторий JCR (например, Jackrabbit).

Ответ 2

По спецификации, единственный "реальный" путь, который вы гарантированно получите из контейнера сервлета, - это временный каталог.

Вы можете получить это через ServletContext.gerAttribute("javax.servlet.context.tempdir"). Однако эти файлы не видны веб-контексту (т.е. Вы не можете опубликовать простой URL-адрес для доставки этих файлов), и эти файлы никоим образом не гарантируют выживание после перезапуска веб-приложения или сервера.

Если вам просто нужно место для хранения рабочего файла в течение короткого времени, то это будет хорошо работать для вас.

Если вам действительно нужен каталог, вы можете сделать его параметром конфигурации (либо переменной среды, либо свойством Java (т.е. java -Dyour.file.here=/tmp/files...), либо параметром контекста, установленным в сети..xml, параметр конфигурации, хранящийся в вашей базе данных через веб-форму и т.д.). Затем это до развертывания, чтобы настроить этот каталог для вас.

Тем не менее, если вам потребуется позднее обработать этот файл, вам понадобится механизм, специфичный для контейнера, для "монтирования" внешних каталогов в ваше веб-приложение (Glassfish как "альтернативные корни документа", у других аналогичные концепции), или вы Нужно написать сервлет/фильтр для обслуживания хранилища файлов за пределами вашего веб-приложения. Этот FileServlet является довольно полным, и, как вы можете видеть, создание собственного, хотя и не сложного, совсем не тривиально, чтобы сделать это правильно.

Редактировать:

Основная суть та же, но вместо использования "getRealPath", просто используйте "getInitParameter".

Так:

String filePath = getServletContext().getInitParameter("storedFilePath") + "/" + fileName;

И быть на вашем пути.

Изменить еще раз:

Что касается содержимого пути, я бы дал ему абсолютный путь. В противном случае вам нужно ЗНАТЬ, где сервер приложений задает свой путь по умолчанию во время выполнения, и каждый сервер приложений вполне может использовать разные каталоги. Например, я считаю, что рабочий каталог для Glassfish является каталогом конфигурации работающего домена. Не особенно очевидный выбор.

Итак, используйте абсолютный путь, наиболее определенно. Таким образом, вы ЗНАЕТЕ, куда будут отправляться файлы, и можете контролировать права доступа на уровне ОС для этого каталога, если это необходимо.