在Android上上传大文件时避免内存溢出错误

26

以下是我的上传代码:

String end = "\r\n";
String twoHyphens = "--";
String boundary = "*****";
try {
    URL url = new URL(ActionUrl);
    HttpURLConnection con = (HttpURLConnection) url.openConnection();
    con.setDoInput(true);
    con.setDoOutput(true);
    con.setUseCaches(false);
    con.setRequestMethod("POST");
    con.setRequestProperty("Connection", "Keep-Alive");
    con.setRequestProperty("Accept", "text/*");
    con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
    DataOutputStream ds = new DataOutputStream(con.getOutputStream());
    ds.writeBytes(twoHyphens + boundary + end);
    ds.writeBytes("Content-Disposition: form-data;" + "name=\"folder\"" + end + end);
    ds.write(SavePath.getBytes("UTF-8"));
    ds.writeBytes(end);
    ds.writeBytes(twoHyphens + boundary + end);
    ds.writeBytes("Content-Disposition: form-data;" + "name=\"Filedata\"; filename=\"");
    ds.write(FileName.getBytes("UTF-8"));
    ds.writeBytes("\"" + end);
    ds.writeBytes(end);
    FileInputStream fStream = new FileInputStream(uploadFilepath+""+FileName);
    int bufferSize = 1024;
    byte[] buffer = new byte[bufferSize];
    int length = -1;
    int pro = 0;
    while((length = fStream.read(buffer)) != -1) {
        ds.write(buffer, 0, length);
    }       
    ds.writeBytes(end);
    ds.writeBytes(twoHyphens + boundary + twoHyphens + end);
    fStream.close();
    ds.flush();
    InputStream is = con.getInputStream();
    int ch;
    StringBuffer b = new StringBuffer();
    while((ch = is.read()) != -1) {
        b.append((char)ch);
    }
    ds.close();
}
catch(Exception e) {
    e.printStackTrace();
}

在小于16 MB 的情况下,上传成功。 但是当大于16 MB 时,将会显示“OutOfMemory”错误。 如何避免 outofmemory 错误?


我想在while((length = fStream.read(buffer)) != -1)循环中每次发送1024字节,但我不知道如何做到。 - brian
Brian,请参考我在这里的答案:http://stackoverflow.com/questions/4455006/posting-a-large-file-in-android - Bob Cheng
嘿@brian,你能发布服务器端代码吗? - Deepak Kumar
3个回答

70

你尝试使用了吗?

con.setChunkedStreamingMode(1024);

以下代码可以帮助您将数据分块成特定大小,这样您就不需要在内存中保留整个文件。

更新:

尝试使用以下方法。我使用此方法上传了一个80 mb的文件而没有出现异常。

public String sendFileToServer(String filename, String targetUrl) {
    String response = "error";
    Log.e("Image filename", filename);
    Log.e("url", targetUrl);
    HttpURLConnection connection = null;
    DataOutputStream outputStream = null;
    // DataInputStream inputStream = null;

    String pathToOurFile = filename;
    String urlServer = targetUrl;
    String lineEnd = "\r\n";
    String twoHyphens = "--";
    String boundary = "*****";
    DateFormat df = new SimpleDateFormat("yyyy_MM_dd_HH:mm:ss");

    int bytesRead, bytesAvailable, bufferSize;
    byte[] buffer;
    int maxBufferSize = 1 * 1024;
    try {
        FileInputStream fileInputStream = new FileInputStream(new File(
                pathToOurFile));

        URL url = new URL(urlServer);
        connection = (HttpURLConnection) url.openConnection();

        // Allow Inputs & Outputs
        connection.setDoInput(true);
        connection.setDoOutput(true);
        connection.setUseCaches(false);
        connection.setChunkedStreamingMode(1024);
        // Enable POST method
        connection.setRequestMethod("POST");

        connection.setRequestProperty("Connection", "Keep-Alive");
        connection.setRequestProperty("Content-Type",
                "multipart/form-data;boundary=" + boundary);

        outputStream = new DataOutputStream(connection.getOutputStream());
        outputStream.writeBytes(twoHyphens + boundary + lineEnd);

        String connstr = null;
        connstr = "Content-Disposition: form-data; name=\"uploadedfile\";filename=\""
                + pathToOurFile + "\"" + lineEnd;
        Log.i("Connstr", connstr);

        outputStream.writeBytes(connstr);
        outputStream.writeBytes(lineEnd);

        bytesAvailable = fileInputStream.available();
        bufferSize = Math.min(bytesAvailable, maxBufferSize);
        buffer = new byte[bufferSize];

        // Read file
        bytesRead = fileInputStream.read(buffer, 0, bufferSize);
        Log.e("Image length", bytesAvailable + "");
        try {
            while (bytesRead > 0) {
                try {
                    outputStream.write(buffer, 0, bufferSize);
                } catch (OutOfMemoryError e) {
                    e.printStackTrace();
                    response = "outofmemoryerror";
                    return response;
                }
                bytesAvailable = fileInputStream.available();
                bufferSize = Math.min(bytesAvailable, maxBufferSize);
                bytesRead = fileInputStream.read(buffer, 0, bufferSize);
            }
        } catch (Exception e) {
            e.printStackTrace();
            response = "error";
            return response;
        }
        outputStream.writeBytes(lineEnd);
        outputStream.writeBytes(twoHyphens + boundary + twoHyphens
                + lineEnd);

        // Responses from the server (code and message)
        int serverResponseCode = connection.getResponseCode();
        String serverResponseMessage = connection.getResponseMessage();
        Log.i("Server Response Code ", "" + serverResponseCode);
        Log.i("Server Response Message", serverResponseMessage);

        if (serverResponseCode == 200) {
            response = "true";
        }

        String CDate = null;
        Date serverTime = new Date(connection.getDate());
        try {
            CDate = df.format(serverTime);
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("Date Exception", e.getMessage() + " Parse Exception");
        }
        Log.i("Server Response Time", CDate + "");

        filename = CDate
                + filename.substring(filename.lastIndexOf("."),
                        filename.length());
        Log.i("File Name in Server : ", filename);

        fileInputStream.close();
        outputStream.flush();
        outputStream.close();
        outputStream = null;
    } catch (Exception ex) {
        // Exception handling
        response = "error";
        Log.e("Send file Exception", ex.getMessage() + "");
        ex.printStackTrace();
    }
    return response;
}

1
出现了错误 java.io.FileNotFoundException: http://ip/web/jquery/uploader/uploadify.php。 - brian
1
如果我想向多部分实体添加更多部分,我只需要向输出流添加换行符并添加所需的数据即可。比如说,我想添加一个名为“json”的JSON字符串和另一个文件。 - meh
请问您能否提供这段代码的服务器端脚本? - Jamil
1
我在我的代码中放置了 setChunkedStreamingMode(1024);,结果出现了以下信息: HTTP/1.1 400 Bad Request Content-Length: 11 Content-Type: text/plain Bad Request - Fábio Filho
@AndroSelva,你的代码只有在我删除connection.setChunkedStreamingMode(1024);后才能工作,但删除后我无法上传大文件。怎么办? - Pablo Escobar
显示剩余9条评论

1
如果服务器接受分块传输模式,您可以使用:
((HttpURLConnection) con).setChunkedStreamingMode(chunkLength)

否则,您可以使用。
((HttpURLConnection) con).setChunkedStreamingMode(0);

或者

/* compute first your request content length
   contentLength = formBodyLength + yourFileSize
*/
con.setRequestProperty("Content-Length", String.valueOf(contentLength));
((HttpURLConnection) con).setFixedLengthStreamingMode(contentLength);

最终...发送你想要的。

-5
bitmap=Bitmap.createScaledBitmap(bitmap, 100, 100, true);

ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); //compress to format you want.
byte [] byte_arr = stream.toByteArray();  
String image_str = Base64.encodeBytes(byte_arr);

我尝试过的最好的方法,对我来说非常成功!!!


压缩方法链接:https://developer.android.com/reference/android/graphics/Bitmap.html - chance
1
问题不是关于图像,而是关于一般文件。 - Ricardo A.
虽然这段代码可能解决了问题,但如果能包括一个解释,说明如何以及为什么这样解决问题将有助于提高您的帖子质量。请记住,您正在回答未来读者的问题,而不仅仅是现在提问的人。请编辑您的答案以添加解释,并指出适用的限制和假设。 - user17242583

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接