Как загрузить файл с помощью библиотеки Java HttpClient, работающей с PHP
Я хочу написать Java-приложение, которое будет загружать файл на сервер Apache с PHP. В коде Java используется библиотека Jakarta HttpClient версии 4.0 beta2:
import java.io.File;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.FileEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;
public class PostFile {
public static void main(String[] args) throws Exception {
HttpClient httpclient = new DefaultHttpClient();
httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
HttpPost httppost = new HttpPost("http://localhost:9002/upload.php");
File file = new File("c:/TRASH/zaba_1.jpg");
FileEntity reqEntity = new FileEntity(file, "binary/octet-stream");
httppost.setEntity(reqEntity);
reqEntity.setContentType("binary/octet-stream");
System.out.println("executing request " + httppost.getRequestLine());
HttpResponse response = httpclient.execute(httppost);
HttpEntity resEntity = response.getEntity();
System.out.println(response.getStatusLine());
if (resEntity != null) {
System.out.println(EntityUtils.toString(resEntity));
}
if (resEntity != null) {
resEntity.consumeContent();
}
httpclient.getConnectionManager().shutdown();
}
}
PHP-файл upload.php
очень просто:
<?php
if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
echo "File ". $_FILES['userfile']['name'] ." uploaded successfully.\n";
move_uploaded_file ($_FILES['userfile'] ['tmp_name'], $_FILES['userfile'] ['name']);
} else {
echo "Possible file upload attack: ";
echo "filename '". $_FILES['userfile']['tmp_name'] . "'.";
print_r($_FILES);
}
?>
Читая ответ, я получаю следующий результат:
executing request POST http://localhost:9002/upload.php HTTP/1.1
HTTP / 1.1 200 ОК Возможная атака загрузки файла: имя файла ''. массив ()
Итак, запрос был успешным, я смог связаться с сервером, однако PHP не заметил файл - метод is_uploaded_file
возвращенный false
а также $_FILES
переменная пуста. Я понятия не имею, почему это могло произойти. Я отследил HTTP-ответ и запрос, и они выглядят нормально:
запрос:
POST /upload.php HTTP / 1.1 Контент-длина: 13091 Тип содержимого: двоичный / октетный поток Хост: localhost:9002 Подключение: Keep-Alive Пользователь-агент: Apache-HttpClient/4.0-beta2 (Java 1.5) Ожидайте: 100-Продолжить ˙Ř˙ŕ..... остальная часть двоичного файла...
и ответ:
HTTP / 1.1 100 Продолжить HTTP / 1.1 200 ОК Дата: ср, 01 июля 2009 06:51:57 GMT Сервер: Apache/2.2.8 (Win32) DAV/2 mod_ssl/2.2.8 OpenSSL/0.9.8g mod_autoindex_color PHP/5.2.5 mod_jk/1.2.26 X-Powered-By: PHP/5.2.5 Длина контента: 51 Keep-Alive: тайм-аут =5, максимум =100 Подключение: Keep-Alive Тип контента: текст / HTML Возможная атака загрузки файла: имя файла ''. Array ()
Я тестировал это как на локальном Windows XP с xampp, так и на удаленном сервере Linux. Я также пытался использовать предыдущую версию HttpClient - версию 3.1 - и результат был еще более неясным, is_uploaded_file
возвращенный false
, тем не мение $_FILES
массив был заполнен правильными данными.
10 ответов
Хорошо, код Java, который я использовал, был неправильным, вот правильный класс Java:
import java.io.File;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;
public class PostFile {
public static void main(String[] args) throws Exception {
HttpClient httpclient = new DefaultHttpClient();
httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
HttpPost httppost = new HttpPost("http://localhost:9001/upload.php");
File file = new File("c:/TRASH/zaba_1.jpg");
MultipartEntity mpEntity = new MultipartEntity();
ContentBody cbFile = new FileBody(file, "image/jpeg");
mpEntity.addPart("userfile", cbFile);
httppost.setEntity(mpEntity);
System.out.println("executing request " + httppost.getRequestLine());
HttpResponse response = httpclient.execute(httppost);
HttpEntity resEntity = response.getEntity();
System.out.println(response.getStatusLine());
if (resEntity != null) {
System.out.println(EntityUtils.toString(resEntity));
}
if (resEntity != null) {
resEntity.consumeContent();
}
httpclient.getConnectionManager().shutdown();
}
}
обратите внимание, используя MultipartEntity.
Обновление для тех, кто пытается использовать MultipartEntity
...
org.apache.http.entity.mime.MultipartEntity
устарел в 4.3.1.
Ты можешь использовать MultipartEntityBuilder
создать HttpEntity
объект.
File file = new File();
HttpEntity httpEntity = MultipartEntityBuilder.create()
.addBinaryBody("file", file, ContentType.create("image/jpeg"), file.getName())
.build();
Для пользователей Maven класс доступен в следующей зависимости (почти такой же, как в ответе fervisa, только в более поздней версии).
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.3.1</version>
</dependency>
Правильный способ будет состоять в том, чтобы использовать многочастный метод POST. Смотрите здесь пример кода для клиента.
Для PHP есть много учебных пособий. Это первое, что я нашел. Я рекомендую сначала протестировать код PHP с помощью html-клиента, а затем попробовать java-клиент.
Я столкнулся с той же проблемой и обнаружил, что имя файла необходимо для работы httpclient 4.x с PHP-бэкэндом. Это не относится к httpclient 3.x.
Поэтому мое решение - добавить параметр name в конструктор FileBody. ContentBody cbFile = new FileBody(file, "image/jpeg", "FILE_NAME");
Надеюсь, поможет.
Более новая версия примера здесь.
Ниже приведена копия оригинального кода:
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.examples.entity.mime;
import java.io.File;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
/**
* Example how to use multipart/form encoded POST request.
*/
public class ClientMultipartFormPost {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.out.println("File path not given");
System.exit(1);
}
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
HttpPost httppost = new HttpPost("http://localhost:8080" +
"/servlets-examples/servlet/RequestInfoExample");
FileBody bin = new FileBody(new File(args[0]));
StringBody comment = new StringBody("A binary file of some kind", ContentType.TEXT_PLAIN);
HttpEntity reqEntity = MultipartEntityBuilder.create()
.addPart("bin", bin)
.addPart("comment", comment)
.build();
httppost.setEntity(reqEntity);
System.out.println("executing request " + httppost.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httppost);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
HttpEntity resEntity = response.getEntity();
if (resEntity != null) {
System.out.println("Response content length: " + resEntity.getContentLength());
}
EntityUtils.consume(resEntity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
}
Вот мое рабочее решение для отправки изображения с постом с использованием библиотек Apache http (очень важно, это добавление границы. В моем случае без него работать не будет):
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] imageBytes = baos.toByteArray();
HttpClient httpclient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(StaticData.AMBAJE_SERVER_URL + StaticData.AMBAJE_ADD_AMBAJ_TO_GROUP);
String boundary = "-------------" + System.currentTimeMillis();
httpPost.setHeader("Content-type", "multipart/form-data; boundary="+boundary);
ByteArrayBody bab = new ByteArrayBody(imageBytes, "pic.png");
StringBody sbOwner = new StringBody(StaticData.loggedUserId, ContentType.TEXT_PLAIN);
StringBody sbGroup = new StringBody("group", ContentType.TEXT_PLAIN);
HttpEntity entity = MultipartEntityBuilder.create()
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
.setBoundary(boundary)
.addPart("group", sbGroup)
.addPart("owner", sbOwner)
.addPart("image", bab)
.build();
httpPost.setEntity(entity);
try {
HttpResponse response = httpclient.execute(httpPost);
...then reading response
Ааа, вам просто нужно добавить параметр имени в
FileBody constructor. ContentBody cbFile = new FileBody(file, "image/jpeg", "FILE_NAME");
Надеюсь, поможет.
Я знал, что я опоздал на вечеринку, но ниже приведен правильный способ справиться с этим, ключ должен использовать InputStreamBody
на месте FileBody
загрузить файл из нескольких частей.
try {
HttpClient httpclient = new DefaultHttpClient();
HttpPost postRequest = new HttpPost("https://someserver.com/api/path/");
postRequest.addHeader("Authorization",authHeader);
//don't set the content type here
//postRequest.addHeader("Content-Type","multipart/form-data");
MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
File file = new File(filePath);
FileInputStream fileInputStream = new FileInputStream(file);
reqEntity.addPart("parm-name", new InputStreamBody(fileInputStream,"image/jpeg","file_name.jpg"));
postRequest.setEntity(reqEntity);
HttpResponse response = httpclient.execute(postRequest);
}catch(Exception e) {
Log.e("URISyntaxException", e.toString());
}
Если вы тестируете это на локальном WAMP, вам может потребоваться настроить временную папку для загрузки файлов. Вы можете сделать это в вашем файле PHP.ini:
upload_tmp_dir = "c:\mypath\mytempfolder\"
Вам нужно будет предоставить разрешения для папки, чтобы разрешить загрузку - разрешение, которое вам нужно предоставить, зависит от вашей операционной системы.
Для тех, кому трудно реализовать принятый ответ (который требует org.apache.http.entity.mime.MultipartEntity), вы можете использовать org.apache.httpcomponents 4.2. * В этом случае вам необходимо явно установить зависимостьhttpmime, в мое дело:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.2.5</version>
</dependency>