Как загрузить файл java

Обновлено: 07.07.2024

Загрузка и выгрузка файлов и общие задачи в веб-приложении Java . Так как в последнее время я много писал о java-сервлете , я подумал предоставить пример примера загрузки и выгрузки файла с использованием сервлета.

Случай использования

Мы создадим динамический веб-проект в Eclipse, и структура проекта будет выглядеть как на картинке ниже.

Давайте рассмотрим все компоненты нашего веб-приложения и разберемся с реализацией.

HTML-страница для загрузки файла

Мы можем загрузить файл на сервер, отправив запрос на отправку сервлету и отправив форму. Мы не можем использовать метод GET для загрузки файла. Еще один момент, на который следует обратить внимание, это то, что тип формы должен быть multipart / form-data . Чтобы выбрать файл из файловой системы пользователя, нам нужно использовать элемент ввода с типом как файл . Таким образом, у нас может быть простая HTML-страница для загрузки файла:

index.html

< form action = "UploadDownloadFileServlet" method = "post" enctype = "multipart/form-data" > Select File to Upload:< input type = "file" name = "fileName" >

Расположение файла сервера

Нам нужно сохранить файл в некотором каталоге на сервере, мы можем жестко закодировать этот каталог в программе, но для большей гибкости мы будем сохранять его настраиваемым в параметрах контекста дескриптора развертывания. Также мы добавим html-страницу нашего файла загрузки в список файлов приветствия.

Наш файл web.xml будет выглядеть следующим образом:

web.xml

< web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns = "http://java.sun.com/xml/ns/javaee" xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version = "3.0" > < display-name >ServletFileUploadDownloadExample</ display-name >

Реализация ServletContextListener

Поскольку нам нужно прочитать параметр контекста для местоположения файла и создать из него объект File, мы можем написать ServletContextListener, чтобы сделать это при инициализации контекста. Мы можем установить абсолютное местоположение каталога и объект File как атрибут контекста, который будет использоваться другими сервлетами.

Наш код реализации ServletContextListener подобен ниже.

public class FileLocationContextListener implements ServletContextListener < public void contextInitialized(ServletContextEvent servletContextEvent) < ServletContext ctx = servletContextEvent.getServletContext(); String relativePath = ctx.getInitParameter( "tempfile.dir" ); File file = new File(rootPath + File.separator + relativePath); System.out.println( "File Directory created to be used for storing files" ); ctx.setAttribute( "FILES_DIR" , rootPath + File.separator + relativePath); public void contextDestroyed(ServletContextEvent servletContextEvent) <

Для загрузки файлов мы будем использовать утилиту Apache Commons FileUpload , для нашего проекта мы используем версию 1.3, FileUpload зависит от jar Apache Commons IO , поэтому нам нужно поместить оба файла в каталог lib проекта, как вы можете видеть выше. изображение для структуры проекта.

Поскольку целью сервлета является загрузка файла, мы переопределим метод init () для инициализации DiskFileItemFactory объекта DiskFileItemFactory сервлета. Мы будем использовать этот объект в реализации метода doPost () для загрузки файла в каталог сервера.

Как только файл будет успешно загружен, мы отправим клиенту ответ с URL-адресом для его загрузки, поскольку HTML-ссылки используют метод GET, мы добавим параметр для имени файла в URL-адрес и можем использовать тот же метод doGet () сервлета, чтобы реализовать процесс загрузки файла.

Для реализации сервлета загрузки файла сначала откроем InputStream для файла и используем метод ServletContext.getMimeType (), чтобы получить MIME-тип файла и установить его в качестве типа содержимого ответа.

Как только мы закончим с настройкой конфигурации ответа, мы можем прочитать содержимое файла из InputStream и записать его в ServletOutputStream и очистить вывод для клиента.

Наша окончательная реализация сервлета UploadDownloadFileServlet выглядит следующим образом.

ОТВЕТЫ

Ответ 1

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

Проверьте больше об этом здесь.

Примечание. Третий параметр в TransferFrom - это максимальное количество байтов для передачи. Integer.MAX_VALUE будет передавать не более 2 ^ 31 байта, Long.MAX_VALUE не более 2 ^ 63 байта (больше, чем любой существующий файл).

Ответ 2

Используйте apache commons-io, только один код строки:

Ответ 3

Упрощение использования nio:

Ответ 4

Вам нужно обработать исключения, возможно, внешние для этого метода.

Ответ 5

Загрузка файла требует, чтобы вы его прочитали, в любом случае вам придется каким-то образом пройти через файл. Вместо строки за строкой вы можете просто прочитать ее байтами из потока:

Ответ 6

Это старый вопрос, но вот краткое, удобочитаемое решение только для JDK с правильно закрытыми ресурсами:

Две строки кода и никаких зависимостей.

Ответ 7

При использовании Java 7+ используйте следующий метод, чтобы загрузить файл из Интернета и сохранить его в каком-либо каталоге:

Ответ 8

Этот ответ почти точно подобен выбранному ответу, но с двумя улучшениями: это метод и он закрывает объект FileOutputStream:

Ответ 9

Ответ 10

Ответ 11

Это еще один вариант java7, основанный на ответе Брайана Риск с использованием инструкции try-with:

Ответ 12

Здесь метод, который не генерирует исключения для сетевых ошибок (только для действительно исключительных проблем, таких как неверный URL-адрес или проблемы с записью в файл)

Ответ 13

Существует проблема с простым использованием:

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

Ответ 14

В отличие от одной строки кода:

этот код даст вам больше контроля над процессом и позволит указать не только тайм-ауты, но User-Agent и Referer значения, которые имеют решающее значение для многих веб-сайтов.

Ответ 15

В библиотеке underscore-java есть метод U.fetch(url).

Ответ 16

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

(Помечается как wiki сообщества, не стесняйтесь добавлять информацию или исправления)

Ответ 17

Ответ 18

Вы можете сделать это в 1 строке, используя netloader для Java:

Ответ 19

Если вы находитесь за прокси-сервером, вы можете установить прокси-серверы в java-программе, как показано ниже:

Если вы не находитесь за прокси-сервером, не включайте строки в свой код. Полный рабочий код для загрузки файла, когда вы находитесь за прокси.

Ответ 20

Ниже приведен пример кода для загрузки фильма из Интернета с использованием кода Java:

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

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

2. Использование Java IO

Увеличение производительности происходит за счет буферизации. При чтении одного байта за раз с помощью метода read() каждый вызов метода подразумевает системный вызов базовой файловой системы. Когда JVM вызывает системный вызов read () , контекст выполнения программы переключается из пользовательского режима в режим ядра и обратно.

Это переключение контекста дорого с точки зрения производительности. Когда мы читаем большое количество байтов, производительность приложения будет низкой из-за большого количества переключений контекста.

Для записи байтов, считанных с URL-адреса, в ваш локальный файл мы будем использовать метод write() из класса FileOutputStream :

При использовании BufferedInputStream метод read() будет считывать столько байтов, сколько мы задали для размера буфера. В нашем примере мы уже делаем это, читая блоки по 1024 байта за раз, поэтому BufferedInputStream не нужен.

Приведенный выше пример очень многословен, но, к счастью, начиная с Java 7, у нас есть класс Files , который содержит вспомогательные методы для обработки операций ввода-вывода. Мы можем использовать метод Files.copy () , чтобы прочитать все байты из InputStream и скопировать их в локальный файл:

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

К счастью, Java предлагает нам пакет NIO, который имеет методы для передачи байтов непосредственно между 2 каналами без буферизации.

Мы подробно рассмотрим это в следующем разделе.

3. Использование NIO

Пакет Java NIO предлагает возможность передачи байтов между 2 каналами без буферизации их в память приложения.

Чтобы прочитать файл с нашего URL-адреса, мы создадим новый ReadableByteChannel из URL потока:

Байты, считанные из ReadableByteChannel , будут переданы в FileChannel , соответствующий файлу, который будет загружен:

Мы будем использовать метод transferFrom() из класса ReadableByteChannel для загрузки байтов с заданного URL-адреса в наш файловый канал :

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

В системах Linux и UNIX эти методы используют нулевая копия метод, который уменьшает количество переключений контекста между режимом ядра и пользовательским режимом.

4. Использование Библиотек

В приведенных выше примерах мы видели, как мы можем загружать контент с URL-адреса, просто используя функциональность ядра Java. Мы также можем использовать функциональность существующих библиотек, чтобы облегчить нашу работу, когда настройки производительности не нужны.

Например, в реальном сценарии нам нужно, чтобы наш код загрузки был асинхронным.

Мы могли бы обернуть всю логику в Вызываемый , или мы могли бы использовать для этого существующую библиотеку.

Загруженный контент будет помещен в FileOutputStream :

Bytebuffer преимущество заключается в том, что память выделяется за пределами кучи JVM, поэтому она не влияет на память приложений.

4.2. Apache Commons IO

Другой широко используемой библиотекой для операций ввода-вывода является Apache Commons IO . Из Javadoc мы видим, что существует служебный класс с именем FileUtils , который используется для общих задач обработки файлов.

Чтобы загрузить файл с URL-адреса, мы можем использовать этот однострочный:

С точки зрения производительности этот код такой же, как и тот, который мы проиллюстрировали в разделе 2.

Базовый код использует те же концепции чтения в цикле некоторых байтов из InputStream и записи их в OutputStream .

Одно из отличий заключается в том, что здесь класс URLConnection используется для управления тайм-аутами подключения, чтобы загрузка не блокировалась в течение большого количества времени:

5. Возобновляемая Загрузка

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

Давайте перепишем первый пример из предыдущих, чтобы добавить эту функциональность.

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

Здесь происходит то, что мы настроили URLConnection для запроса байтов файла в определенном диапазоне . Диапазон будет начинаться с последнего загруженного байта и заканчиваться на байте, соответствующем размеру удаленного файла.

Другим распространенным способом использования заголовка Range является загрузка файла по частям путем установки различных диапазонов байтов. Например, для загрузки файла размером 2 КБ мы можем использовать диапазон 0 – 1024 и 1024 – 2048.

Еще одно тонкое отличие от кода в разделе 2. заключается в том, что FileOutputStream открывается с параметром append , установленным в true :

После того, как мы внесли это изменение, остальная часть кода идентична той, которую мы видели в разделе 2.

6. Заключение

В этой статье мы рассмотрели несколько способов загрузки файла с URL-адреса на Java.

Наиболее распространенной реализацией является та, в которой мы буферизуем байты при выполнении операций чтения/записи. Эта реализация безопасна для использования даже для больших файлов, потому что мы не загружаем весь файл в память.

Мы также видели, как мы можем реализовать загрузку с нулевой копией с помощью Java NIO Channels . Это полезно, поскольку сводит к минимуму количество переключений контекста, выполняемых при чтении и записи байтов, а при использовании прямых буферов байты не загружаются в память приложения.

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

Наконец, мы поговорим о том, как возобновить загрузку в случае сбоя подключения до того, как будет прочитан весь файл.

2. Использование Java IO

Самый простой API, который мы можем использовать для загрузки файла, - это Java IO. Мы можем использовать класс URL, чтобы открыть соединение с файлом, который мы хотим загрузить. Чтобы эффективно прочитать файл, мы будем использовать метод openStream () для получения InputStream:

При чтении из InputStream рекомендуется обернуть его в BufferedInputStream, чтобы повысить производительность.

Увеличение производительности происходит за счет буферизации. При чтении по одному байту с использованием метода read () каждый вызов метода подразумевает системный вызов базовой файловой системы. Когда JVM вызывает системный вызов read () , контекст выполнения программы переключается из пользовательского режима в режим ядра и обратно.

Это переключение контекста дорого с точки зрения производительности. Когда мы читаем большое количество байтов, производительность приложения будет низкой из-за большого количества задействованных переключений контекста.

Для записи байтов, прочитанных из URL-адреса, в наш локальный файл мы будем использовать метод write () из класса FileOutputStream :

При использовании BufferedInputStream метод read () будет читать столько байтов, сколько мы установили для размера буфера. В нашем примере мы уже делаем это, читая блоки по 1024 байта за раз, поэтому BufferedInputStream не нужен.

Приведенный выше пример очень подробный, но, к счастью, начиная с Java 7 у нас есть класс Files, который содержит вспомогательные методы для обработки операций ввода-вывода. Мы можем использовать метод Files.copy () для чтения всех байтов из InputStream и копирования их в локальный файл:

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

К счастью, Java предлагает нам пакет NIO, в котором есть методы для передачи байтов напрямую между 2 каналами без буферизации.

Подробности мы рассмотрим в следующем разделе.

3. Использование NIO

Пакет Java NIO предлагает возможность передавать байты между 2 каналами без их буферизации в памяти приложения.

Чтобы прочитать файл по нашему URL, мы создадим новый ReadableByteChannel из потока URL :

Байты, считанные из ReadableByteChannel, будут переданы в FileChannel, соответствующий файлу, который будет загружен:

Мы будем использовать метод transferFrom () из класса ReadableByteChannel для загрузки байтов с заданного URL-адреса в наш FileChannel :

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

В системах Linux и UNIX эти методы используют технику нулевого копирования , которая уменьшает количество переключений контекста между режимом ядра и пользовательским режимом.

4. Использование библиотек

В приведенных выше примерах мы видели, как можно загружать контент с URL-адреса, просто используя основные функции Java. Мы также можем использовать функциональность существующих библиотек, чтобы упростить нашу работу, когда настройки производительности не нужны.

Например, в реальном сценарии нам нужно, чтобы код загрузки был асинхронным.

Мы могли бы обернуть всю логику в Callable или использовать для этого существующую библиотеку.

Загруженный контент будет помещен в FileOutputStream :

Преимущество ByteBuffers заключается в том, что память выделяется вне кучи JVM, поэтому это не влияет на память приложений.

4.2. Apache Commons IO

Еще одна широко используемая библиотека для операций ввода-вывода - Apache Commons IO. Из документации Javadoc видно, что существует служебный класс с именем FileUtils, который используется для общих задач работы с файлами.

Чтобы загрузить файл по URL-адресу, мы можем использовать этот однострочный файл:

С точки зрения производительности этот код такой же, как и тот, который мы продемонстрировали в разделе 2.

Базовый код использует те же концепции чтения в цикле нескольких байтов из InputStream и записи их в OutputStream .

Одно из отличий заключается в том, что здесь класс URLConnection используется для управления тайм-аутом соединения, чтобы загрузка не блокировалась на большое количество времени:

5. Возобновляемая загрузка

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

Давайте перепишем первый пример из предыдущего, чтобы добавить эту функциональность.

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

What happens here is that we've configured the URLConnection to request the file bytes in a specific range. The range will start from the last downloaded byte and will end at the byte corresponding to the size of the remote file.

Another common way to use the Range header is for downloading a file in chunks by setting different byte ranges. For example, to download 2 KB file, we can use the range 0 – 1024 and 1024 – 2048.

Another subtle difference from the code at section 2. is that the FileOutputStream is opened with the append parameter set to true:

After we've made this change the rest of the code is identical to the one we've seen in section 2.

6. Conclusion

В этой статье мы увидели несколько способов загрузки файла по URL-адресу в Java.

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

Мы также увидели, как можно реализовать загрузку с нулевым копированием с помощью каналов Java NIO . Это полезно, потому что оно минимизировало количество переключений контекста, выполняемых при чтении и записи байтов, а при использовании прямых буферов байты не загружаются в память приложения.

В спецификацию Java EE 6 включены Servlet 3.0, интерфейс «Part» и аннотация @MultiPartConfig, обеспечивающие простой способ загрузки файлов FileUpload на сервер без каких либо сторонних библиотек. Поэтому любая реализация сервлета 3.0 на серверах типа Tomcat 7.х, JBoss 6.х, GlassFish 3.х дают выгоду при загрузке файла.

С выходом очередной версии JSF 2.2 у разработчиков, использующих данный фреймворк, также появилась возможность легкой загрузки файлов на сервер. В данной статье будет рассмотрен пример FileUpload использования компонента h:inputFile для реализации этой возможности.

Структура примера FileUpload

Структура примера включает компонент DemoBean.java, файлы дескриптора web.xml и конфигурации faces-config.xml, страницы выбора файлов index.xhtml и результатов загрузки success.xhtml. В директории lib размещается библиотечный файл фреймворка JSF 2.2. Файлы загружаются в поддиректорию upload проекта. Структура проекта Eclipse представлена на следующем скриншоте.


Листинг компонента DemoBean.java

В компоненте DemoBean.java определены поля файлов (file1, file2) типа Part с методами get/set и метод upload(), который возвращает наименование страницы success без расширения (т.е. без «.xhtml») согласно упрощенной навигации фреймворка JSF. Для «полного» перехода на страницу success.xhtml используется параметр «?faces-redirect=true».

Вспомогательный метод setDirUpload() позволяет определить путь к директории upload приложения через класс ServletContext. Следует обратить внимание, что при работе приложения под управлением IDE (Eclipse) будет определена несколько иная директория, поскольку среда разработки формирует свою рабочую директорию в workspace проектов. Если расскоментировать строку вывода в консоль полного пути директории DIR, то можно будет увидеть что-то подобное
D:\projects\eclipse\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\UploadFile\

Листинг конфигурации приложения faces-config.xml

Файл конфигурации faces-config.xml примера FileUpload не используется, поскольку последние версии допускают облегченную навигацию. В этой связи метод upload() компонента DemoBean возвращает адрес страницы success без расширения.

Листинг дескриптора приложения web.xml

В дескрипторе приложения определяются стандартные параметры - уровень контекста Development, сервлет FacesServlet, timeout 30 и открываемая страница по умолчанию faces/index.xhtml.

Листинг страницы index.xhtml

На странице загрузки файлов на сервер необходимо выбрать файлы и нажать кнопку «Загрузить». Компонент фреймворка JSF h:inputFile работает с файловой системой на компьютера пользователя и позволяет не напрягаясь с помощью мышки выбрать необходимый файл. В атрибуте value необходимо указать соответствующие «объекты» типа Part компонента JavaBean.

Читайте также: