Как загрузить файл с помощью библиотеки 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 OK
Possible file upload attack: filename ''.
Array
(
)

Итак, запрос был успешным, я смог связаться с сервером, однако PHP не заметил файл - метод is_uploaded_file вернул false, а переменная $_FILES пуста. Понятия не имею, почему это могло случиться. Я отследил HTTP-ответ и запрос, и они выглядят нормально:
запрос:

POST /upload.php HTTP/1.1
Content-Length: 13091
Content-Type: binary/octet-stream
Host: localhost:9002
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.0-beta2 (java 1.5)
Expect: 100-Continue

˙Ř˙ŕ..... the rest of the binary file...

и ответ:

HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Date: Wed, 01 Jul 2009 06:51:57 GMT
Server: 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
Content-Length: 51
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html

Possible file upload attack: filename ''.Array
(
)

Я тестировал это как на локальном Windows XP с xampp, так и на удаленном сервере Linux. Я также пробовал использовать предыдущую версию HttpClient - версию 3.1 - и результат был еще более неясным, is_uploaded_file вернул false, однако массив $_FILES был заполнен правильными данными.


person Piotr Kochański    schedule 01.07.2009    source источник
comment
DefaultHttpClient () теперь устарел.   -  person Pranjal Choladhara    schedule 20.01.2015
comment
@PranjalCholadhara, поэтому какой класс следует использовать вместо DefaultHttpClient (), поскольку он устарел?   -  person Pruthvi Chitrala    schedule 26.11.2019


Ответы (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.

person Piotr Kochański    schedule 01.07.2009
comment
Какие новые поддерживаемые версии HttpComponents (т.е. hc.apache.org НЕ HttpClient-3.1) поддерживаются? Я получаю сообщение об ошибке: Не удается разрешить тип org.apache.james.mime4j.message.SingleBody. На него косвенно ссылаются из необходимых файлов .class - person therobyouknow; 17.02.2010
comment
Ответ: HttpClient 4.1-alpha1 и HttpCore 4.1-alpha1 из hc.apache.org/downloads.cgi - поддерживаемый Java-код Apache HttpComponents. С ними это сообщение об ошибке исчезнет :) - person therobyouknow; 17.02.2010
comment
Я использую 4.2, и у меня нет пакета mime. Это было изменено? - person expert; 14.08.2013
comment
Функции MIME Apache HttpComponents можно найти в group: artifact org.apache.httpcomponents:httpmime. - person Jacob Zwiers; 26.08.2013

Обновление для тех, кто пытается использовать _1 _...

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>
person Brent Robinson    schedule 06.01.2014
comment
Возможно, вам стоит обновить принятый ответ и улучшить его. - person Halil; 03.03.2017

Правильный способ - использовать многостраничный метод POST. См. вот например код для клиента.

Для PHP доступно множество руководств. Это первый, который я нашел. Я рекомендую вам сначала протестировать PHP-код с помощью html-клиента, а затем попробовать java-клиент.

person kgiannakakis    schedule 01.07.2009
comment
Я пытался использовать предложенный вами метод, т.е. HttpClient v. 3.1, но is_uploaded_file по-прежнему возвращал false, однако на этот раз массив $ _FILES был заполнен правильными данными, что еще больше озадачило меня. КСТАТИ. Загрузка выполняется на сервере, я протестировал свой файл upload.php, используя простую HTML-форму. - person Piotr Kochański; 01.07.2009

Я столкнулся с той же проблемой и обнаружил, что имя файла требуется для работы httpclient 4.x с серверной частью PHP. Для httpclient 3.x это было не так.

Итак, мое решение - добавить параметр имени в конструктор FileBody. ContentBody cbFile = новый FileBody (файл, «изображение / JPEG», «ИМЯ ФАЙЛА»);

Надеюсь, это поможет.

person gaojun1000    schedule 09.11.2013
comment
Спасибо за помощь. Это меня спасло. Но следует использовать конструктор, который принимает 4 аргумента. новый FileBody (файл, file.getName (), application / octet-stream, UTF-8); Конструктор с тремя аргументами принимает в качестве третьего аргумента mimetype, а не имя. - person MTilsted; 11.01.2014

A пример более новой версии здесь.

Ниже представлена ​​копия исходного кода:

/*
 * ====================================================================
 * 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();
        }
    }

}
person rado    schedule 27.01.2015

Ааа, вам просто нужно добавить параметр имени в

FileBody constructor. ContentBody cbFile = new FileBody(file, "image/jpeg", "FILE_NAME");

Надеюсь, это поможет.

person Community    schedule 24.11.2014

Я знал, что опаздываю на вечеринку, но ниже приведен правильный способ справиться с этим, ключ состоит в том, чтобы использовать 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());
   }
person Dev    schedule 09.06.2016

Если вы тестируете это на своем локальном WAMP, вам может потребоваться настроить временную папку для загрузки файлов. Вы можете сделать это в своем файле PHP.ini:

upload_tmp_dir = "c:\mypath\mytempfolder\"

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

person Fenton    schedule 01.07.2009
comment
tmp установлена ​​папка. Загрузка работает на сервере, я протестировал свой файл upload.php, используя простую HTML-форму. - person Piotr Kochański; 01.07.2009
comment
подскажите, пожалуйста, как написать код сервера на java для получения запроса httpclient - person Aswan; 21.04.2010

Тем, кому сложно реализовать принятый ответ (для чего требуется org.apache.http.entity.mime.MultipartEntity), вы можете использовать org.apache.httpcomponents 4.2. * В этом случае вам необходимо явно установить < / strong> httpmime зависимость, в моем случае:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
    <version>4.2.5</version>
</dependency>
person fervisa    schedule 27.08.2013

Есть мое рабочее решение для отправки изображения с постом с использованием http-библиотек apache (здесь очень важно добавить границы, оно не будет работать без него в моем соединении):

            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
person Krystian    schedule 14.08.2014