Не удается получить доступ к загрузке файлов HTTP POST (Android)

Я разрабатываю приложение для Android, которое позволяет пользователю загружать файл в службы, такие как Twitpic и другие. Загрузка POST выполняется без каких-либо внешних библиотек и работает нормально. Моя единственная проблема заключается в том, что я не могу добиться какого-либо прогресса, потому что вся загрузка выполняется, когда я получаю ответ, а не при записи байтов в выходной поток. Вот что я делаю:

URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setDoInput(true); conn.setDoOutput(true); conn.setUseCaches(false); conn.setRequestMethod("POST"); conn.setRequestProperty("Connection", "Keep-Alive"); conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); DataOutputStream dos = new DataOutputStream(conn.getOutputStream()); 

Затем я записываю данные формы в dos, что сейчас не так важно. После этого я пишу сами данные файла (чтение из «in», которое является InputStream данных, которые я хочу отправить):

 while ((bytesAvailable = in.available()) > 0) { bufferSize = Math.min(bytesAvailable, maxBufferSize); byte[] buffer = new byte[bufferSize]; bytesRead = in.read(buffer, 0, bufferSize); dos.write(buffer, 0, bytesRead); } 

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

 in.close(); dos.flush(); dos.close(); 

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

 DataInputStream inStream = new DataInputStream(conn.getInputStream()); 

Это занимает несколько секунд или минут, в зависимости от того, насколько велик файл и насколько быстро он подключен к Интернету. Мои вопросы теперь: 1) Почему не происходит uplaod, когда я пишу байты в «dos»? 2) Как я могу добиться прогресса, чтобы показать диалог прогресса во время загрузки, когда все это происходит сразу?

/ EDIT: 1) Я установил Content-Length в заголовке, который немного изменил проблему, но никоим образом не решает ее: теперь весь контент загружается после того, как последний байт записан в поток. Таким образом, это не меняет ситуацию, когда вы не можете захватить прогресс, потому что снова данные записываются сразу. 2) Я попробовал MultipartEntity в Apache HttpClient v4. Там у вас нет OutputStream вообще, потому что все данные записываются при выполнении запроса. Так что опять-таки нет никакого способа добиться прогресса.

Есть ли кто-нибудь, кто имеет какую-либо другую идею, как захватить процесс в многостраничной / форме загрузки?

Solutions Collecting From Web of "Не удается получить доступ к загрузке файлов HTTP POST (Android)"

В последние дни я много пробовал, и я думаю, что у меня есть ответ на начальный вопрос:

Невозможно добиться прогресса, используя HttpURLConnection, потому что в Android существует ошибка / необычное поведение: http://code.google.com/p/android/issues/detail?id=3164#c6

Это будет фиксированный пост Froyo, который не может ждать …

Таким образом, единственным другим вариантом, который я нашел, является использование Apache HttpClient. Ответ, связанный с papleu , правильный, но он относится к общей Java. Классы, которые там используются, больше не доступны в Android (HttpClient 3.1 был частью SDK, один раз). Итак, что вы можете сделать, это добавить библиотеки из HttpClient 4 (в частности, apache-mime4j-0.6.jar и httpmime-4.0.1.jar) и использовать комбинацию первого ответа (от Tuler) и последнего ответа (от Hamy ):

 import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntity; public class CountingMultipartEntity extends MultipartEntity { private final ProgressListener listener; public CountingMultipartEntity(final ProgressListener listener) { super(); this.listener = listener; } public CountingMultipartEntity(final HttpMultipartMode mode, final ProgressListener listener) { super(mode); this.listener = listener; } public CountingMultipartEntity(HttpMultipartMode mode, final String boundary, final Charset charset, final ProgressListener listener) { super(mode, boundary, charset); this.listener = listener; } @Override public void writeTo(final OutputStream outstream) throws IOException { super.writeTo(new CountingOutputStream(outstream, this.listener)); } public static interface ProgressListener { void transferred(long num); } public static class CountingOutputStream extends FilterOutputStream { private final ProgressListener listener; private long transferred; public CountingOutputStream(final OutputStream out, final ProgressListener listener) { super(out); this.listener = listener; this.transferred = 0; } public void write(byte[] b, int off, int len) throws IOException { out.write(b, off, len); this.transferred += len; this.listener.transferred(this.transferred); } public void write(int b) throws IOException { out.write(b); this.transferred++; this.listener.transferred(this.transferred); } } } 

Это работает с HttpClient 4, но недостатком является то, что мой apk теперь имеет размер 235 kb (это было 90 kb, когда я использовал многостраничную загрузку, описанную в моем вопросе) и, что еще хуже, размер установленного приложения 735 kb (около 170 kb до). Это ужасно. Только для достижения прогресса при загрузке размера приложения теперь более чем в 4 раза больше, чем раньше.

Попробуйте это. Оно работает.

 connection = (HttpURLConnection) url_stripped.openConnection(); connection.setRequestMethod("PUT"); String boundary = "---------------------------boundary"; String tail = "\r\n--" + boundary + "--\r\n"; connection.addRequestProperty("Content-Type", "image/jpeg"); connection.setRequestProperty("Connection", "Keep-Alive"); connection.setRequestProperty("Content-Length", "" + file.length()); connection.setDoOutput(true); String metadataPart = "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"metadata\"\r\n\r\n" + "" + "\r\n"; String fileHeader1 = "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"uploadfile\"; filename=\"" + fileName + "\"\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Transfer-Encoding: binary\r\n"; long fileLength = file.length() + tail.length(); String fileHeader2 = "Content-length: " + fileLength + "\r\n"; String fileHeader = fileHeader1 + fileHeader2 + "\r\n"; String stringData = metadataPart + fileHeader; long requestLength = stringData.length() + fileLength; connection.setRequestProperty("Content-length", "" + requestLength); connection.setFixedLengthStreamingMode((int) requestLength); connection.connect(); DataOutputStream out = new DataOutputStream( connection.getOutputStream()); out.writeBytes(stringData); out.flush(); int progress = 0; int bytesRead = 0; byte buf[] = new byte[1024]; BufferedInputStream bufInput = new BufferedInputStream( new FileInputStream(file)); while ((bytesRead = bufInput.read(buf)) != -1) { // write output out.write(buf, 0, bytesRead); out.flush(); progress += bytesRead; // update progress bar publishProgress(progress); } // Write closing boundary and close stream out.writeBytes(tail); out.flush(); out.close(); // Get server response BufferedReader reader = new BufferedReader( new InputStreamReader(connection.getInputStream())); String line = ""; StringBuilder builder = new StringBuilder(); while ((line = reader.readLine()) != null) { builder.append(line); } 

Ссылка: http://delimitry.blogspot.in/2011/08/android-upload-progress.html

Можно получить прогресс от DefaultHttpClient. Тот факт, что он остается в линии

 response = defaultHttpClient.execute( httpput, context ); 

Пока не будут отправлены полные данные, необязательно означает, что вы не можете захватить прогресс. А именно, HttpContext изменяется при выполнении этой строки, поэтому, если вы подходите к нему через другой поток, вы можете наблюдать изменения в этом. Вам нужен ConnAdapter. Он встроен в HttpConnectionMetrics

 final AbstractClientConnAdapter connAdapter = (AbstractClientConnAdapter) context.getAttribute(ExecutionContext.HTTP_CONNECTION); final HttpConnectionMetrics metrics = connAdapter.getMetrics(); int bytesSent = (int)metrics.getSentBytesCount(); 

Если вы проверите это периодически, вы увидите прогресс. Обязательно правильно обрабатывайте потоки, и все они стараются / улавливаются. В частности, обрабатывайте случай, когда контекст больше недействителен (IllegalStateException), который возникает, когда Put отменяется или завершается.

Вместо этого используйте WatchedInputStream. Это хорошо, отлично и легко использовать.

  1. Используйте WatchedInputStream для переноса вашего входного потока.
  2. watchedStream.setListener

    WatchedStream.setListener (новый WatchedInputStream.Listener () {

    Public void notify (int read) {// что-то сделать для обновления интерфейса.
    }}