Перерывно `java.net.socketException: Broken Pipe 'во время загрузки Android Multiploart - Corruption Stream или преждеврAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Перерывно `java.net.socketException: Broken Pipe 'во время загрузки Android Multiploart - Corruption Stream или преждевр

Сообщение Anonymous »

Я сталкиваюсь с очень прерывистой java.net.socketException: ошибка сломанной трубы при загрузке нескольких изображений из моего приложения Android Developmnt с использованием httpurlConnection на производственный сервер Java. Основным разочарованием является его непредсказуемость: загрузка иногда добивается успеха (возможно, 1 из 20 попыток) , но большинство попыток преждевременно заканчиваются ошибкой «сломанной трубы» или «сброс соединения». Часто кажется, что клиент даже не заканчивает отправку всех данных до того, как подключение упадет, так как ошибка «сломанная труба» бросается при попытке взаимодействия с подключением после записи (через conn.getResponsecode (); ). Это заставляет меня подозревать тонкую проблему обработки потока на стороне клиента или просчету, которая обычно, но не всегда, приводит к проблеме. Точная точка может варьироваться, иногда, по -видимому, во время тела писать, в других случаях, пытаясь получить ответ. * прерывистый успех: очень редко, всю загрузку завершается, получен 201 от ответа, а изображения правильно отправляются на сервер. Отрезан.
[*] Ошибка кажется более распространенной с большим/большим изображением, но не всегда. Трассировка стека исключений может варьироваться, часто указывая на этап записи (например, в SocketOutputStream.socketWrite , вызываемой из My ConnectionHandler.transferfileto ) или фаза после записи/предварительного ответа (например, httpurlConnectionImpl.getResponsecode CodeShode
Различные тайм-ауты подключения и чтения. (Webmaster сообщает, что время считывания сервера составляет 30 секунд) />
Какие механизмы на стороне клиента могут привести к тому, что выходной поток httpurlconnection преждевременно сломается при написании многочисленного тела, особенно если содержимого мысли

< /ul>
  • ByteArrayOutputStream (baos) Для тела и длины содержимого :
[*] Чтобы устранить потенциальные расхождения в расчете с двойным пассом, я изменил подход, чтобы написать Всего Multy Body в подход. * Затем я использовал baos.size () в качестве длины содержимого .
[*] baos.tobytearray () был записан в httpurlconnection sputstream . Матч тела, что приводит к неизменно успешной загрузке и ответе сервера.
[/list]
  • различное число/размер изображений: с одним маленьким изображением до нескольких больших изображений. Массив изображений, отправляемых на сервер PHP.
  • Обеспечение закрытия потока: используется try-with-resources для всех inputstream и outputstream , чтобы убедиться, что они закрыты правильно. Значения (15-30 секунд).
чего я ожидал: я ожидал, что после правильного построения многочисленного тела и предоставления точного содержимого , клиент успешно передаст все данные на сервер. После этого я ожидал, что при вызове Conn.getResponseCode () . Соединение должно оставаться открытым до тех пор, пока клиент не прочтет этот ответ.
фактический результат (проблема): Вместо этого процесс загрузки весьма прерывист. Это исключение может возникнуть либо, казалось бы, во время написания мультитарного тела в outputStream , либо когда я позже пытаюсь вызвать conn.getResponsecode () .
[*] редко (ок. Основной проблемой является непредсказуемая «сломанная труба» и неспособность надежно заполнить передачу данных и получить ответ сервера, даже при использовании таких методов, как BAOS, которые должны обеспечить точность ..

prong> concectionhandler.java: соответствующий код - пожалуйста, запрашивайте больше, если необходимо p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> p> prongehandler.// In ConnectionHandler.java
package com.example.authtest.Tools;

import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
// ... other necessary imports ...

public class ConnectionHandler {
private static final String TAG = "ConnectionHandler";
private static final String UPLOAD_URL_STRING = "REDACTED";
// Method called by CameraFragment
public void uploadImages(String serviceToken, String username, String jobNum, String maxFileSize, File[] images, boolean overviewShot, boolean checkingIn, String checkingInDate, UploadCallback callback) {
HttpURLConnection conn = null;
String boundary = "Boundary-" + System.currentTimeMillis(); // Unique boundary for each request
try {
// 1. Calculate Content-Length using a "dry run"
long contentLength = constructAndSendMultipartFormData(null, boundary, username, jobNum, maxFileSize, images, overviewShot, checkingIn, checkingInDate);
Log.d(TAG, "Calculated Content-Length: " + contentLength);

URL url = new URL("REDACTED");
URL conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(15000); // 15 seconds
conn.setReadTimeout(30000); // 30 seconds
conn.setRequestMethod("POST");
conn.setRequestProperty("Connection", "close");
conn.setRequestProperty("Authorization", "REDACTED");
conn.setRequestProperty("Cookie", "REDACTED");
conn.setRequestProperty("User-Agent", "MyApp/1.0");
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
conn.setRequestProperty("Content-Length", Long.toString(contentLength));
conn.setDoOutput(true);
conn.setUseCaches(false);

// 2. Write the actual body
OutputStream os = null; // Declare os outside try to check if it was opened
try (OutputStream os = conn.getOutputStream()) {
constructAndSendMultipartFormData(os, boundary, username, jobNum, maxFileSize, images, overviewShot, checkingIn, checkingInDate);
os.flush();
} catch (IOException e) {
Log.e(TAG, "IOException during body write to REDACTED: " + e.getMessage(), e); // If "Broken pipe" happens here, it means connection dropped during write
callback.onUploadError(e);
return; // Don't proceed to getResponseCode
}

// 3. Get response
int responseCode = conn.getResponseCode(); // "Broken pipe" frequently occurs here if not during write Log.d(TAG, "Response Code from REDACTED: " + responseCode); // Logic to read response (success or error stream)

if (responseCode >= 200 && responseCode < 300) {
callback.onUploadComplete("Success - " + responseCode); // Provide more info if needed
} else {
callback.onUploadError(new IOException("Server at REDACTED returned HTTP error: " + responseCode));
}
} catch (IOException e) {
// General IOExceptions, including "Broken pipe" if it happens at getOutputStream() or getResponseCode()
Log.e(TAG, "General IOException during upload to REDACTED: " + e.getMessage(), e);
callback.onUploadError(e);
} finally {
if (conn != null) {
conn.disconnect();
}
}
}

// Dual-purpose method: calculates length if outputStream is null, otherwise writes.
private long constructAndSendMultipartFormData(OutputStream outputStream, String boundary, String username, String jobNum, String maxFileSize, File[] images, boolean overviewShot, boolean checkingIn, String checkingInDate) throws IOException {
long totalLength = 0L; final String CRLF = "\r\n";
final byte[] CRLF_BYTES = CRLF.getBytes(StandardCharsets.UTF_8);
totalLength += writeFormField(outputStream, boundary, "username", username);
totalLength += writeFormField(outputStream, boundary, "jobNum", jobNum);

String usernamePartHeader = "--" + boundary + CRLF + "Content-Disposition: form-data; name=\"username\"" + CRLF + CRLF;
byte[] usernamePartHeaderBytes = usernamePartHeader.getBytes(StandardCharsets.UTF_8);
byte[] usernameBytes = username.getBytes(StandardCharsets.UTF_8);
totalLength += usernamePartHeaderBytes.length;
totalLength += usernameBytes.length;
totalLength += CRLF_BYTES.length;
if (outputStream != null) {
outputStream.write(usernamePartHeaderBytes);
outputStream.write(usernameBytes);
outputStream.write(CRLF_BYTES);
}

String overviewShotStr = overviewShot ? "1" : "0";
String overviewShotHeader = "--" + boundary + CRLF + "Content-Disposition: form-data; name=\"overviewShot\"" + CRLF + CRLF;
byte[] overviewShotHeaderBytes = overviewShotHeader.getBytes(StandardCharsets.UTF_8); byte[] overviewShotBytes = overviewShotStr.getBytes(StandardCharsets.UTF_8);
totalLength += overviewShotHeaderBytes.length + overviewShotBytes.length + CRLF_BYTES.length;
if (outputStream != null) {
outputStream.write(overviewShotHeaderBytes);
outputStream.write(overviewShotBytes);
outputStream.write(CRLF_BYTES);
}

// Image files
if (images != null) {
for (File imageFile : images) {
if (imageFile == null || !imageFile.exists() || imageFile.length() == 0) {
Log.w(TAG, "Skipping invalid or empty image file: " + (imageFile != null ? imageFile.getName() : "null file"));
continue;
}
String fieldNameInForm = "technicianPictures";
String originalFileName = imageFile.getName();
String filePartHeader = "--" + boundary + CRLF + "Content-Disposition: form-data; name=\"" + fieldNameInForm + "\"; filename=\"" + originalFileName + "\"" + CRLF + "Content-Type: image/jpeg" + CRLF + // Assuming JPEG, make dynamic if other types
"Content-Transfer-Encoding: binary" + CRLF + // Optional, usually implied for files
CRLF; // Empty line before file data
byte[] filePartHeaderBytes = filePartHeader.getBytes(StandardCharsets.UTF_8);
totalLength += filePartHeaderBytes.length;
if (outputStream != null) {
outputStream.write(filePartHeaderBytes);
}
long fileLength = transferFileTo(imageFile, outputStream);
totalLength += fileLength; // CRLF after file data
totalLength += CRLF_BYTES.length;
if (outputStream != null) {
outputStream.write(CRLF_BYTES);
}
}
}

// End boundary: --boundary--CRLF
String endBoundaryStr = "--" + boundary + "--" + CRLF;
byte[] endBoundaryBytes = endBoundaryStr.getBytes(StandardCharsets.UTF_8);
totalLength += endBoundaryBytes.length;
if (outputStream != null) {
outputStream.write(endBoundaryBytes);
}
return totalLength;
}

// Dual-purpose file transfer: returns length if outStream is null, otherwise writes.
private long transferFileTo(File file, OutputStream outStream) throws IOException {
if (outStream == null) {
// Calculate length only
if (!file.isFile()) return 0L;
return file.length();
}

// Write mode
long writtenLength = 0L;
if(file.isFile()) {
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buffer = new byte[4096]; // Common buffer size
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead); // "Broken pipe" can occur here during the stream
writtenLength += bytesRead;
}
}
}
Log.d(TAG, "Transferred " + writtenLength + " bytes for file " + file.getName());
return writtenLength;
}

// Callback interface for upload status
public interface UploadCallback {
void onUploadComplete(String response);
void onUploadError(Exception e);
}
}
< /code>

camerafragment.java: соответствующий код - пожалуйста, попросите больше, если необходимо < /strong> < /li>
< /ul>
// In CameraFragment.java
// ... (Initializes File[] capturedImages and imageCount) ...
// Inside an upload button's OnClickListener:
// ... (Code to get user credentials, job details, etc., as Strings) ...
String authToken = ...;
String currentUsername = ...;
String currentJobNumber = ...;
String maxFileSizeAllowed = ...; // e.g., "10485760"
File[] imagesToUploadArray = new File[imageCount];
if (imageCount > 0) {
System.arraycopy(capturedImages, 0, imagesToUploadArray, 0, imageCount);
}

new Thread(() -> {
try { // Simplified call to ConnectionHandler
connectionHandler.uploadImages(authToken, currentUsername, currentJobNumber, maxFileSizeAllowed, imagesToUploadArray, isOverviewShot, isCheckingIn, checkingInDate, new ConnectionHandler.UploadCallback() {
@Override public void onUploadComplete(String response) {
// Update UI: Upload successful
Log.d("CameraFragment", "Upload successful: " + response);
}
@Override
public void onUploadError(Exception e) {
// Update UI: Upload failed
Log.e("CameraFragment", "Upload error reported by callback", e);
// This is where the "Broken pipe" or other IOExceptions often land
}
});
} catch (Exception e) {
Log.e("CameraFragment", "Exception launching upload thread", e);
}
}).start();


Подробнее здесь: https://stackoverflow.com/questions/797 ... ltipart-up
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»