Как насчет:
import copy
d = { ... }
d2 = copy.deepcopy(d)
Python 2 или 3:
Python 3.2 (r32:88445, Feb 20 2011, 21:30:00) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import copy
>>> my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}
>>> my_copy = copy.deepcopy(my_dict)
>>> my_dict['a'][2] = 7
>>> my_copy['a'][2]
3
>>>
Для просмотра и выбора файла для загрузки вам необходимо поле HTML в форме. Как указано в спецификации HTML, вы должны использовать метод
POST
, а атрибут enctype
формы должен быть установлен в "multipart/form-data"
.
<form action="upload" method="post" enctype="multipart/form-data">
<input type="text" name="description" />
<input type="file" name="file" />
<input type="submit" />
</form>
После отправки такой формы двоичные данные многочастной формы доступны в теле запроса в другом формате, чем когда enctype
не установлен.
До версии Servlet 3.0 Servlet API не поддерживал multipart/form-data
. Он поддерживал только стандартный enctype формы application/x-www-form-urlencoded
. request.getParameter()
и его аналоги возвращают null
при использовании данных формы multipart. Именно здесь на помощь приходит хорошо известный Apache Commons FileUpload.
Теоретически вы можете самостоятельно разобрать тело запроса на основе ServletRequest#getInputStream()
. Однако это точная и утомительная работа, требующая точного знания RFC2388. Не стоит пытаться сделать это самостоятельно или копировать код без библиотеки, найденный где-либо в Интернете. Многие онлайновые источники потерпели в этом неудачу, например roseindia.net. См. также загрузка файла pdf. Вам лучше использовать настоящую библиотеку, которую используют (и неявно тестируют!) миллионы пользователей в течение многих лет. Такая библиотека доказала свою надежность.
Если вы используете как минимум Servlet 3.0 (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3 и т.д.), то вы можете просто использовать стандартный API, предоставляемый HttpServletRequest#getPart()
для сбора отдельных элементов данных многочастной формы (большинство реализаций Servlet 3.0 фактически используют Apache Commons FileUpload под прикрытием для этого!). Также обычные поля формы доступны с помощью getParameter()
обычным способом.
Сначала аннотируйте ваш сервлет с @MultipartConfig
для того, чтобы он распознавал и поддерживал multipart/form-data
запросы и таким образом заставил работать getPart()
:
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
// ...
}
Затем реализуйте его doPost()
следующим образом:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String description = request.getParameter("description"); // Retrieves <input type="text" name="description">
Part filePart = request.getPart("file"); // Retrieves <input type="file" name="file">
String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
InputStream fileContent = filePart.getInputStream();
// ... (do your job here)
}
Обратите внимание на Path#getFileName()
. Это исправление MSIE для получения имени файла. Этот браузер неправильно отправляет полный путь к файлу вместе с именем вместо только имени файла.
В случае если у вас есть для многофайловой загрузки, соберите их как показано ниже (к сожалению, нет такого метода как
request. getParts("file")
):
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// ...
List<Part> fileParts = request.getParts().stream().filter(part -> "file".equals(part.getName())).collect(Collectors.toList()); // Retrieves <input type="file" name="file" multiple="true">
for (Part filePart : fileParts) {
String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
InputStream fileContent = filePart.getInputStream();
// ... (do your job here)
}
}
Обратите внимание, что Part#getSubmittedFileName()
был введен в Servlet 3.1 (Tomcat 8, Jetty 9, WildFly 8, GlassFish 4 и т.д.). Если вы еще не на Servlet 3.1, то вам нужен дополнительный метод утилиты для получения имени переданного файла.
private static String getSubmittedFileName(Part part) {
for (String cd : part.getHeader("content-disposition").split(";")) {
if (cd.trim().startsWith("filename")) {
String fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
return fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
}
}
return null;
}
String fileName = getSubmittedFileName(filePart);
Обратите внимание на исправление в MSIE в части получения имени файла. Этот браузер неправильно отправляет полный путь к файлу вместе с именем, а не только имя файла.
Если вы еще не на Servlet 3.0 (не пора ли перейти на новую версию?), общепринятой практикой является использование Apache Commons FileUpload для разбора запросов данных многокомпонентных форм. У него есть отличное Руководство пользователя и FAQ (внимательно изучите оба). Существует также MultipartRequest
от O'Reilly ("cos"), но он имеет некоторые (незначительные) ошибки и уже несколько лет активно не поддерживается. Я бы не рекомендовал его использовать. Apache Commons FileUpload все еще активно поддерживается и в настоящее время очень развит.
Для того чтобы использовать Apache Commons FileUpload, вам необходимо иметь в /WEB-INF/lib
вашего webapp по крайней мере следующие файлы:
Ваша первая попытка не удалась, скорее всего, потому что вы забыли commons IO.
Вот пример того, как может выглядеть doPost()
вашего UploadServlet
при использовании Apache Commons FileUpload:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
for (FileItem item : items) {
if (item.isFormField()) {
// Process regular form field (input type="text|radio|checkbox|etc", select, etc).
String fieldName = item.getFieldName();
String fieldValue = item.getString();
// ... (do your job here)
} else {
// Process form file field (input type="file").
String fieldName = item.getFieldName();
String fileName = FilenameUtils.getName(item.getName());
InputStream fileContent = item.getInputStream();
// ... (do your job here)
}
}
} catch (FileUploadException e) {
throw new ServletException("Cannot parse multipart request.", e);
}
// ...
}
Очень важно, чтобы вы не вызывали getParameter()
, getParameterMap()
, getParameterValues()
, getInputStream()
, getReader()
и т.д. в одном запросе заранее. В противном случае контейнер сервлетов прочитает и разберет тело запроса, и таким образом Apache Commons FileUpload получит пустое тело запроса. См. также a.o. ServletFileUpload#parseRequest(request) возвращает пустой список.
Обратите внимание на FilenameUtils#getName()
. Это исправление MSIE для получения имени файла. Этот браузер неправильно отправляет полный путь к файлу вместе с именем вместо только имени файла.
В качестве альтернативы вы также можете обернуть все это в Filter
, который автоматически разберет все это и поместит материал обратно в параметрическую карту запроса, чтобы вы могли продолжить использовать request.getParameter()
обычным способом и получить загруженный файл с помощью request.getAttribute()
. Пример можно найти в этой статье блога.
getParameter()
все еще возвращает null
Обратите внимание, что версии Glassfish старше 3.1.2 имели ошибку, когда getParameter()
все еще возвращал null
. Если вы нацелены на такой контейнер и не можете его обновить, то вам нужно извлечь значение из getPart()
с помощью этого метода утилиты:
private static String getValue(Part part) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(part.getInputStream(), "UTF-8"));
StringBuilder value = new StringBuilder();
char[] buffer = new char[1024];
for (int length = 0; (length = reader.read(buffer)) > 0;) {
value.append(buffer, 0, length);
}
return value.toString();
}
String description = getValue(request.getPart("description")); // Retrieves <input type="text" name="description">
getRealPath()
или part.write()
! )Обратитесь к следующим ответам для получения подробной информации о правильном сохранении полученного InputStream
(переменная fileContent
, как показано в приведенных выше фрагментах кода) на диск или в базу данных:
Для получения подробной информации о правильном обслуживании сохраненного файла с диска или базы данных обратно клиенту перейдите к следующим ответам:
Перейдите к следующим ответам, как загружать изображения с помощью Ajax (и jQuery). Обратите внимание, что код сервлета для сбора данных формы для этого менять не нужно! Можно изменить только способ ответа, но это довольно тривиально (т.е. вместо пересылки в JSP просто выведите JSON или XML или даже простой текст, в зависимости от того, что ожидает скрипт, отвечающий за вызов Ajax).
Надеюсь, это все поможет :)