2

我编写了 Android 代码以通过 HTTP Put 将大文件(10MB+)上传到 Apache PHP 服务器(我不能使用 FTP,因为我的应用程序有时在移动网络上运行,而且我听说许多移动运营商不允许 FTP-如果我错了,请纠正我)。处理实际上传的函数由 AsyncTask 调用,因此不会导致应用程序挂起。但是,我对上传的方法有些担心——有时我上传不完整,有时会出现“误报”——我收到一个信号,表明文件已上传,只是检查并看不到任何内容。

我的 C 语言背景比 Java 更深,而且我对 Java 的自动内存管理并不完全清楚,所以如果我的问题看起来有些荒谬,请原谅我。

所以,这是我的问题:

1) 上传问题可能是由内存问题引起的吗?该代码最初由原始开发人员编写以一次读取所有内容,但我在下面重新编写了它以使用 ByteArrayOutputStream 并逐块读取文件。我仍然担心 InputStream 试图在初始化时立即读取所有内容。我只是误解了 InputStream,还是它的行为更像 C 中的文件指针?

2)我不喜欢在while循环中每次都调用new——我怀疑,就像在C中一样,新内存一直在分配,但我不确定在重新初始化之前调用null是否真的有帮助。这会导致这里泄漏吗?我之所以问,是因为我认为 Java 的内存管理以某种方式确保不允许更多内存进入堆并跟踪引用。

3) 如果手机进入待机状态,则上传停止。发生这种情况时,是否有办法让上传继续进行?

4) PHP 服务器代码在报告网络错误/问题方面是否可能缺少某些内容?我已经尽可能多地包含了处理写入错误的内容,但我觉得不知何故这可以写得更好。

此外,我并不局限于使用 HttpPut——这只是向我建议的一种方法。我愿意在服务器端进行更改以支持更好的文件传输方法。

Android Java 代码(上传客户端中的函数):

private boolean transmitBytes(Uri uri) 
{
        /**/
        ByteArrayEntity requestEntity;
        InputStream inputStream = null;
        float temp = 0.0f;
        float temp1 = 0.0f;
        long total = 0;
        int buffSize = 102400;
        byte[] buffer;
        try {
            inputStream = getContentResolver().openInputStream(uri);
            ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
            if (length <= 1048576) {
                buffer = new byte[buffSize];
            } else {
                buffer = new byte[bufferSize];
            }
            // we need to know how may bytes were read to write them to the byteBuffer
            int len = 0;

            sharedPreferences = getSharedPreferences("Upload", MODE_PRIVATE);
            String userid = sharedPreferences.getString("userid", ""); // Set the first segment with
                                                                       // the resume flag at 0 so
                                                                       // wipes anything of that
                                                                       // filename on
            // srvr.
            String putURL = Constants.UPLOAD_MEDIA_URL;
            putURL += "mediatype=";
            putURL += mediatype;
            putURL += "&file_name=";
            putURL += new File(vidFileName).getName();
            putURL += "&usrname=";
            putURL += userid;
            putURL += "&filesize=";
            putURL += Long.toString(length);
            putURL += "&sha256sum=";
            putURL += cksum;
            putURL += "&resume=0";
            putURL = putURL.replace(" ", "");
            // Read the first segment for transmission.

            if ((len = inputStream.read(buffer)) != -1) {

                byteBuffer.write(buffer, 0, len);

                // Dump first segment into requestEntity
                requestEntity = new ByteArrayEntity(byteBuffer.toByteArray());

                // Wipe byte buffer after you are done writing with it to clear out contents.
                byteBuffer.reset();

                // Transmit segment.
                HttpPut httpPut = new HttpPut(putURL);

                httpPut.setEntity(requestEntity);

                DefaultHttpClient httpClient = new DefaultHttpClient();

              //Set timeouts. If no response by timeout, mark failure.
                HttpParams httpParameters = new BasicHttpParams();

                // The default value is zero, that means the timeout is not used and we go to infinite.
                //Set to 3000 ms here.
                   int timeoutConnection = 5000;
                   HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);

                // in milliseconds which is the timeout for waiting for data. If not set, will be infinite.
                   int timeoutSocket = 5000;
                   HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);

                httpClient.setParams(httpParameters);

                // Transmit via PUT
                HttpResponse response = httpClient.execute(httpPut);
                HttpEntity entity = response.getEntity();
                entity.getContent();

                // Set resume tag to 1.
                putURL = putURL.replaceAll("&resume=0", "&resume=1");
            }
            // write the remaining segments, (bufferSize) at a time
            while ((len = inputStream.read(buffer)) != -1) {

                byteBuffer.write(buffer, 0, len);

                requestEntity = new ByteArrayEntity(byteBuffer.toByteArray());

                // Wipe byte buffer after you are done writing with it to clear out contents.
                byteBuffer.reset();

                HttpPut httpPut = new HttpPut(putURL);

                httpPut.setEntity(requestEntity);


                DefaultHttpClient httpClient = new DefaultHttpClient();

                //Set timeouts. If no response by timeout, mark failure.
                HttpParams httpParameters = new BasicHttpParams();

                // The default value is zero, that means the timeout is not used. Set to 3000 ms here.
                   int timeoutConnection = 3000;
                   HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);

                // in milliseconds which is the timeout for waiting for data.
                   int timeoutSocket = 5000;
                   HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);

                httpClient.setParams(httpParameters);

                // Transmit via PUT
                HttpResponse response = httpClient.execute(httpPut);
                HttpEntity entity = response.getEntity();
                InputStream is = entity.getContent();
                String result = convertStreamToString(is);

        //9/10/2013: TODO-check for response codes in addition to these. Maybe, there is an error code being returned?
                if (result.contains("0") || result.contains("1")) {
                    total += len;

                    temp = total * 100;
                    temp1 = temp / length;

                    //Updates object within async task.
                    mProgressDialog.setProgress((int) temp1);
                }

                else
                {
                    /* Because this is controlled by an AsyncTask, we must bubble up that
                     * there has been a failure in the system if the user is unable to upload. 
                     * 
                     */
                     return false;
                }/**/



            }
            return true;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        } 


        catch (ConnectTimeoutException e ){
              //Took too long to connect to remote host
            e.printStackTrace();
            return false;
            }

            //Handled already by IOException.
            catch (SocketTimeoutException e){
              //Remote host didn’t respond in time
                e.printStackTrace();
                return false;
            }/**/

        //In case anything else is missing...
        catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                if (null != inputStream)
                    inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
    }

PHP服务器端代码:

<?php

      $CHUNK = 8192;
      //$header_size=1024;

      //7/16/2012: this will now be set to 0 by default, and only to 1 upon complete transmission of file.
      $rslt=0;
      $bytes_written=0;


    //set error handler
    set_error_handler("errorLogFunction");

      try {

          //Error handler test
          //echo($test);

          if (!($putData = fopen("php://input", "r")))
          {
            //throw new Exception ("Can't get PUT data.");
            $log = new KLogger ( "putfile_php_log.txt" , KLogger::DEBUG );

            // Do database work that throws an exception
            $log->LogError("Can't get PUT data.");

            // Print out some information
            $log->LogInfo($errMsg);
          }

          else
          {

            $filedata_arr=explode("&",$filedata);

            $mediadata_tmp=explode("=",$filedata_arr[0]);

            $filetype=$mediadata_tmp[1];

            $filename_tmp=explode("=",$filedata_arr[1]);

            $filename=$filename_tmp[1];

            $username_tmp=explode("=",$filedata_arr[2]);

            $usr=$username_tmp[1];

            $filesize_tmp=explode("=",$filedata_arr[3]);

            $filesize=(int)$filesize_tmp[1];

            $sha256sum_tmp=explode("=",$filedata_arr[4]);

            $sha256sum=$sha256sum_tmp[1];

            //echo "SHA read: " . $sha256sum . "<BR>";

            $resume_tmp=explode("=",$filedata_arr[5]);

            $resume=$resume_tmp[1];

              //6/18/2013: Replace any spaces with underscores. For now, don't add date to filename.

              $tmp2=ereg_replace(" ","_",$filename);

              $targetFilename=$_SERVER['DOCUMENT_ROOT'] . '/received/'.$filetype.'/'.$usr."_".$tmp2;

              //7/27/2012: Check if the user exists-if the user does not exist, do not allow the upload. Do this only once-if the file exists, don't run this check.
              if (!file_exists($targetFilename))
              {
                  if (checkIfUsernameExists($usr)==0)
                  {
                      $rslt=-9;
                  }
              }

            //Continue on only if file could be written
            if ($rslt>=0)
            {
                // Open the file for writing
                if ($resume==0)
                {
                    if (!($fp = fopen($targetFilename, "w")))
                      {
                          //throw new Exception ("Can't write to tmp file");
                          //echo json_encode(-1);
                          //throw new Exception ("Can't get PUT data.");
                            $log = new KLogger ( "putfile_php_log.txt" , KLogger::DEBUG );

                            // Do database work that throws an exception
                            $log->LogError("Can't write to tmp file.");

                            // Print out some information
                            $log->LogInfo($errMsg);
                          $rslt=-1;
                      }
                }

                else
                {
                      if (!($fp = fopen($targetFilename, "a")))
                      {
                          //throw new Exception ("Can't write to tmp file");
                          //throw new Exception ("Can't get PUT data.");
                            $log = new KLogger ( "putfile_php_log.txt" , KLogger::DEBUG );

                            // Do database work that throws an exception
                            $log->LogError("Can't append to tmp file.");

                            // Print out some information
                            $log->LogInfo($errMsg);

                          //echo json_encode(-2);
                          $rslt=-2;
                      }

                      else
                      {
                        $bytes_written=filesize($targetFilename);
                      }
                }

                if ($rslt>=0)
                {
                    // Read the data a chunk at a time and write to the file.
                    while ($data = fread($putData, $CHUNK)) 
                    {
                      $chunk_read = strlen($data);
                      if (($block_write = fwrite($fp, $data)) != $chunk_read)
                        {
                            //throw new Exception ("Can't write more to tmp file");

                            //throw new Exception ("Can't get PUT data.");
                            $log = new KLogger ( "putfile_php_log.txt" , KLogger::DEBUG );

                            // Do database work that throws an exception
                            $log->LogError("Can't write more to tmp file.");

                            // Print out some information
                            $log->LogInfo($errMsg);
                            //echo json_encode(-3);
                            $rslt=-3;
                            break;
                        }

                      $bytes_written += $block_write;

                      //7/2/2012: Commented out temporarily until resume upload feature supported.
                      //echo "<BR>" . $tot_write . " written.";
                      //echo $tot_write;
                    }

                    if ( ! fclose($fp) )
                    {
                      //throw new Exception ("Can't close tmp file");
                      //echo json_encode(-4);

                      //throw new Exception ("Can't get PUT data.");
                        $log = new KLogger ( "putfile_php_log.txt" , KLogger::DEBUG );

                        // Do database work that throws an exception
                        $log->LogError("Can't close tmp file.");

                        // Print out some information
                        $log->LogInfo($errMsg);

                      $rslt=-4;
                    }

                    unset($putData);

                     // now the params can be used like any other variable
                    // see below after input has finished

                        // Check file length and SHA-256

                        //6/16/2012: no need to do filesize check anymore. Just do checksum.
                        /*if ($tot_write != $file_size)
                        {
                          throw new Exception ("Wrong file size");
                          //echo json_encode(-6);
                          $rslt=-6;
                        }*/

                        if (($rslt>=0) && ($bytes_written==$filesize))
                        {

                            //7/27/2012: Skipping checksum check because of iOS. Will consider adding another flag for checksum later.
                            /*$sha256_arr = explode(' ',exec("sha256sum $targetFilename"));
                            $sha256 = $sha256_arr[0];

                            if ($sha256 != $sha256sum) 
                            {
                              //throw new Exception ("Wrong sha256");
                              //echo json_encode(-7);
                              $rslt=-7;
                            }

                            //if the checksums and filesizes match, return success.
                            else
                            {
                                $rslt=1;
                            }*/

                            //echo "<BR>Calculated SHA-256: " . $sha256 . "<BR>";
                            //echo "Read in SHA-256: " . $sha256sum . "<BR>";

                            $rslt=1;
                        }

                        //If the filesize and checksum match, send the messages indicating success.
                }




                }

            }//if (put data)

          }//try

        catch (Exception $e) 
        {
            //var_dump($e->getMessage());
            //echo json_encode(-5);
            $rslt=-5;
        }






        echo json_encode($rslt);
?>
4

0 回答 0