草庐IT

android - 计算 Base64 编码文件的 Post Content-Length 大小

coder 2023-12-23 原文

我正在尝试将一些大文件从 Android 设备上传到 .Net Web 服务。此 Web 服务已设置为接受这些文件作为 POST 参数,并且文件必须作为 Base64 编码字符串发送。

我已经能够使用 Christian d'Heureuse 的 this 库将文件转换为 Base64 字符串,以字节为单位计算字符串的大小并在之前发送它,但是我之前使用的方法涉及加载整个文件处理大文件时导致内存不足错误的内存,这并不意外。

我一直在尝试将文件分块转换为 Base64,并在转换时通过连接(使用数据输出流对象)流式传输此数据,因此不需要将整个文件一次性加载到内存中去吧,但是我似乎无法在转换文件之前准确计算出请求的 Content-Length 的大小 - 我通常看起来大约有 10 个字节 - 令人沮丧的是,它偶尔会起作用!

我还发现,有时当这确实有效时,服务器会返回以下错误消息“Base64 字符数组的大小无效”。我认为这是填充字符的问题,但是我看不出我的代码有问题可以解决这个问题,非常感谢关于这个问题的一些建议!

这是生成请求和流式传输数据的代码:

    try
{


    HttpURLConnection connection = null;

    DataOutputStream outputStream = null;

    DataInputStream inputStream = null;

    //This is the path to the file
    String pathToOurFile = Environment
            .getExternalStorageDirectory().getPath()
            + "/path/to/the/file.zip";

    String urlServer = "https://www.someserver.com/somewebservice/";

    int bytesRead, bytesAvailable, bufferSize;

    byte[] buffer;

    int maxBufferSize = 456;


    //The parameters of the POST request - File Data is the file in question as a Base64 String
    String params = "Username=foo&Password=bar&FileData=";

    File sizeCheck = new File(pathToOurFile);

    Integer zipSize = (int) sizeCheck.length();

    Integer paddingRequired = ((zipSize * 8) / 6) % 3;

    Integer base64ZipSize = ((zipSize * 8) / 6)
            + ((zipSize * 8) / 6) % 3;

    Integer paramLength = params.getBytes().length;

    //Code to work out the number of lines required, assuming we create a new
    //line every 76 characters - this is used t work out the number of
    //extra bytes required for new line characters
    Integer numberOfLines = base64ZipSize / 76;

    Log.i(TAG, "numberOfLines: " + numberOfLines);

    Integer newLineLength = System.getProperty("line.separator")
            .getBytes().length;

    //This works out the total length of the Content
    Integer totalLength = paramLength + base64ZipSize
            + (numberOfLines * newLineLength) + paddingRequired;

    Log.i(TAG, "total Length: " + totalLength);

    FileInputStream fileInputStream = new FileInputStream(new File(
            pathToOurFile));

    URL url = new URL(urlServer);

    connection = (HttpURLConnection) url.openConnection();

    connection.setDoInput(true);

    connection.setDoOutput(true);

    connection.setUseCaches(false);

    connection.setRequestMethod("POST");

    connection.setRequestProperty("Connection", "Keep-Alive");

    connection.setRequestProperty("Content-Type",
            "application/x-www-form-urlencoded;");

    connection.setRequestProperty("Content-Length", ""
            + totalLength); // number of bytes

    outputStream = new DataOutputStream(
            connection.getOutputStream());

    //Write out the parameters to the data output stream
    outputStream.writeBytes(params);

    bytesAvailable = fileInputStream.available();

    bufferSize = Math.min(bytesAvailable, maxBufferSize);

    buffer = new byte[bufferSize];

    bytesRead = fileInputStream.read(buffer, 0, bufferSize);

    Integer totalSent = paramLength;

    Integer enLen = 0;


    //Convert the file to Base64 and Stream the result to the
    //Data output stream
    while (bytesRead > 0)
    {
        String convetedBase64 = Base64Coder.encodeLines(buffer);
        convetedBase64 = convetedBase64.replace("=", "");

        if (totalSent >= (totalLength - 616))
        {
            Log.i(TAG, "about to send last chunk of data");
            convetedBase64 = convetedBase64.substring(0,
                    convetedBase64.length() - 1);
        }

        Log.i(TAG,
                "next data chunk to send: "
                        + convetedBase64.getBytes().length);
        Log.i(TAG, "'" + convetedBase64 + "'");

        enLen = enLen + convetedBase64.length();
        outputStream.writeBytes(convetedBase64);

        totalSent = totalSent + convetedBase64.getBytes().length;

        Log.i(TAG, "total sent " + totalSent);
        Log.i(TAG, "actual size: " + outputStream.size());

        bytesAvailable = fileInputStream.available();
        bufferSize = Math.min(bytesAvailable, maxBufferSize);
        buffer = new byte[bufferSize];
        bytesRead = fileInputStream.read(buffer, 0, bufferSize); // read
                                                                    // into
                                                                    // the
                                                                    // buffer
    }

    Log.i(TAG, "enLen: " + enLen);
    Log.i(TAG, "paddingRequired: " + paddingRequired);

    for (int x = 0; x < paddingRequired; x++)
    {
        outputStream.writeBytes("=");
    }

    InputStream is2 = connection.getInputStream();
    String output = IOUtils.toString(is2);
    Log.i(TAG, "Got server response: " + output);
    fileInputStream.close();
    outputStream.flush();
    outputStream.close();
}
catch (Exception ex)
{
    Log.e(TAG, "caught an exception:" + ex.getMessage());
}

如果有人能指出我的代码中可能导致此问题的任何错误,或者建议更好的转换和上传文件的方法,我将不胜感激。

最佳答案

太棒了...我确实设法找到了解决这个问题的一些方法,以防万一有人偶然发现这个问题我会把它们留在这里:

第一个是将数据写出到一个临时文件,这样我就可以在转换后以字节为单位获得大小 - 起初似乎是个好主意,但效率低下,在发现其他方法后似乎很愚蠢。

另一种方法是不指定内容长度(我不知道你可以这样做!)。不幸的是,Android 仍然试图为上传分配足够的内存,这导致了问题。

如果您指定使用 ChunkedStreamingMode 的连接,Android 会很好地播放并缓冲上传,节省使用的 ram(除了 2.2 上的一个奇怪错误)。

代码如下:

httppost.setDoInput(true);
httppost.setDoOutput(true);
httppost.setRequestMethod("POST");
httppost.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
httppost.setChunkedStreamingMode(0); //This specifies the size of the chunks - 0 uses the system default

DataOutputStream dos = new DataOutputStream(httppost.getOutputStream());
dos.writeBytes(content); //This code write out the rest of the post body first, for example username or password

try
{
    BufferedInputStream dis = new BufferedInputStream(new FileInputStream("path/to/some/file"), 2048);

    byte[] in = new byte[512];

    while (dis.read(in)) > 0)
    {
        dos.write(Base64.encode(in, Base64.URL_SAFE)); //This writes out the Base64 data
                                                       //I used the web safe base64 to save URL encoding it again
                                                       //However your server must support this
        dos.write(newLine); //this write out a newline character
    }

dos.flush();
dis.close();
dos.close();

希望对您有所帮助!!!

关于android - 计算 Base64 编码文件的 Post Content-Length 大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12553127/

有关android - 计算 Base64 编码文件的 Post Content-Length 大小的更多相关文章

  1. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

  2. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  3. ruby - 用逗号、双引号和编码解析 csv - 2

    我正在使用ruby​​1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\

  4. ruby-on-rails - 使用一系列等级计算字母等级 - 2

    这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,

  5. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  6. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  7. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  8. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  9. ruby-on-rails - 有没有一种工具可以在编码时自动保存对文件的增量更改? - 2

    我最喜欢的Google文档功能之一是它会在我工作时不断自动保存我的文档版本。这意味着即使我在进行关键更改之前忘记在某个点进行保存,也很有可能会自动创建一个保存点。至少,我可以将文档恢复到错误更改之前的状态,并从该点继续工作。对于在MacOS(或UNIX)上运行的Ruby编码器,是否有具有等效功能的工具?例如,一个工具会每隔几分钟自动将Gitcheckin我的本地存储库以获取我正在处理的文件。也许我有点偏执,但这点小保险可以让我在日常工作中安心。 最佳答案 虚拟机有些人可能讨厌我对此的回应,但我在编码时经常使用VIM,它具有自动保存功

  10. ruby - 使用 Ruby,计算 n x m 数组的每一列中有多少个 true 的简单方法是什么? - 2

    给定一个nxmbool数组:[[true,true,false],[false,true,true],[false,true,true]]有什么简单的方法可以返回“该列中有多少个true?”结果应该是[1,3,2] 最佳答案 使用转置得到一个数组,其中每个子数组代表一列,然后将每一列映射到其中的true数:arr.transpose.map{|subarr|subarr.count(true)}这是一个带有inject的版本,应该在1.8.6上运行,没有任何依赖:arr.transpose.map{|subarr|subarr.in

随机推荐