Envíe archivos grandes (de vídeo) desde Android a PHP Server

Estoy tratando de enviar un archivo de vídeo de mi dispositivo Android a un servidor (que utiliza PHP para procesar la solicitud). He utilizado volley para otras solicitudes que he hecho a ese mismo servidor, pero cuando intento enviar un archivo de vídeo (como una cadena Base64) al servidor, obtengo un OutOfMemoryError. Me preguntaba si había una manera de enviar el archivo encima de una manera diferente (quizá usando una corriente).

Este es mi método actual para enviar el archivo de vídeo:

private void syncFullVideos() { checkPermissions(); if (pDialog == null || !pDialog.isShowing()) { pDialog = new ProgressDialog(this); pDialog.setCanceledOnTouchOutside(false); pDialog.setMessage(getString(R.string.pDialogSync)); pDialog.show(); } syncSucces = new boolean[syncVideos.size()]; Arrays.fill(syncSucces, Boolean.FALSE); for (final Video video: syncVideos) { String picturepath = video.getImage_path(); if (fileExists(picturepath)) { File videoFile = new File(picturepath); FileInputStream fin = null; byte[] byte_arr = null; try { fin = new FileInputStream(videoFile); byte_arr = new byte[(int)videoFile.length()]; fin.read(byte_arr); } catch (IOException e) { e.printStackTrace(); } final String image_str = Base64.encodeToString(byte_arr, 0); Log.d("SyncFullVideos", video.toString()); String tag_string_req = "string_req"; final String TAG = AppController.class .getSimpleName(); String url = "http://android.diggin.io/diggin/v1/videos"; StringRequest strReq = new StringRequest(Request.Method.PUT, url, new Response.Listener<String>() { @Override public void onResponse(String response) { Log.d(TAG, response); try { final JSONObject jsonObject = new JSONObject(response); if (!jsonObject.getBoolean("error4")) { int index = jsonObject.getInt("index"); syncSucces[index] = true; Log.e("AddSucces(" + (addCount + 1) + "/" + (syncVideos.size()) + ")", video.toString()); addCount++; if (addCount == syncVideos.size()) { refreshPhotos(); } } else { Log.e("AddFail(" + (addCount + 1) + "/" + (syncVideos.size()) + ")", video.toString()); addCount++; if (addCount == syncVideos.size()) { refreshPhotos(); } } } catch (JSONException e) { e.printStackTrace(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { VolleyLog.d(TAG, "Error: " + error.getMessage()); //Send message when something goes wrong runOnUiThread(new Runnable() { @Override public void run() { pDialog.hide(); AlertDialog.Builder dlgAlert = new AlertDialog.Builder(PictureActivity.this); dlgAlert.setMessage(R.string.errFullSyncVids); dlgAlert.setPositiveButton(R.string.errBtnOK, null); dlgAlert.setCancelable(true); dlgAlert.create().show(); } }); } }) { @Override protected Map<String, String> getParams() { Map<String, String> params = new HashMap<>(); params.put("id", String.valueOf(video.getId())); params.put("im_path", video.getDBImage_path()); params.put("image", image_str); params.put("index", String.valueOf(syncVideos.indexOf(video))); return params; } }; // Adding request to request queue AppController.getInstance().addToRequestQueue(strReq, tag_string_req); } } } 

Y este es el PHP que uso para procesar la solicitud y enviar una respuesta (estoy usando la API REST):

 $app->put('/videos', function() use ($app) { // check for required params verifyRequiredParams(array('id','im_path','image','index')); $response = array(); $id = $app->request->put('id'); $im_path = $app->request->put('im_path'); $image = $app->request->put('image'); $index = $app->request->put('index'); $db = new DbHandler(); // update photo $result = $db->updateVideo($id, $im_path); if ($result) { $response["error"] = false; $response["message"] = "Video updated successfully"; // Decode Image $binary=base64_decode($image); header('Content-Type: video/mp4'); $im_path2 = explode("_",$im_path); $im_path2[0] .= 's'; $im_path2[2] = $im_path; $im_path3 = implode("/",$im_path2); $filepath = '../images/'.$im_path3; $dirname = dirname($filepath); if (!is_dir($dirname)) { if(mkdir($dirname, 0777, true)) { $response["error2"] = false; $response["message2"] = "Directory Created Succesfully"; } else { $response["error2"] = true; $response["error3"] = true; $response["error4"] = true; $response["message2"] = "Failed to create directory. Please try again"; $response["filepath"] = $filepath; } } // Images will be saved under images folder $file = fopen($filepath, 'wb'); if ($file == false) { $response["error3"] = true; $response["error4"] = true; $response["message3"] = "Failed to open file. Please try again"; $response["filepath"] = $filepath; echoRespnse(200, $response); } else { // Create File $response["error3"] = false; $response["message3"] = "File is open and good to write"; fwrite($file, $binary); fclose($file); if (file_exists($filepath)) { $response["error4"] = false; $response["message4"] = "File exists"; $response["index"] = $index; echoRespnse(201, $response); } else { $response["error4"] = true; $response["message4"] = "File doesn't exist"; echoRespnse(200, $response); } } } else { $response["error"] = true; $response["error2"] = true; $response["error3"] = true; $response["error4"] = true; $response["message"] = "Failed to update video. Please try again"; $response["id"] = $id; $response["im_path"] = $im_path; echoRespnse(200, $response); } }); 

Después de probar una gran cantidad de cosas diferentes que encontré una manera utilizando HttpUrlConnection, aquí está la nueva versión del método syncFullVideos:

 private void syncFullVideos() { new StreamFileTask().execute(); } private class StreamFileTask extends AsyncTask<Void,Integer,Void> { protected Void doInBackground(Void... params) { syncSucces2 = new boolean[syncVideos.size()]; Arrays.fill(syncSucces2, Boolean.FALSE); for (int i = 0; i < syncVideos.size(); i++) { Video video = syncVideos.get(i); String picturepath = video.getImage_path(); if (fileExists(picturepath)) { File sourceFile = new File(picturepath); String fileName = video.getDBImage_path(); int id = video.getId(); int index = syncVideos.indexOf(video); HttpURLConnection conn; DataOutputStream dos; int bytesRead, bytesAvailable, bufferSize; byte[] buffer; int maxBufferSize = 1024 * 1024; int serverResponseCode; Log.e("VideoUpload", "Uploading: sourcefileURI, " + fileName); if (!sourceFile.isFile()) { Log.e("uploadFile", "Source File not exist"); } else { try { FileInputStream fin = new FileInputStream(sourceFile); URL url = new URL("http://android.diggin.io/diggin/v1/vidUpload.php"); Log.v("VideoUpload", url.toString()); // Open a HTTP connection to the URL conn = (HttpURLConnection) url.openConnection(); conn.setDoInput(true); // Allow Inputs conn.setDoOutput(true); // Allow Outputs conn.setUseCaches(false); // Don't use a Cached Copy conn.setRequestMethod("POST"); conn.setRequestProperty("Connection", "Keep-Alive"); conn.setRequestProperty("ENCTYPE", "multipart/form-data"); conn.setRequestProperty("Content-Type", "multipart/form-data"); conn.setRequestProperty("X_FILE_NAME", fileName); conn.setRequestProperty("VID_ID", String.valueOf(id)); conn.setRequestProperty("VID_INDEX", String.valueOf(index)); conn.setRequestProperty("CONTENT_LENGTH", String.valueOf(sourceFile.length())); publishProgress(2, i); dos = new DataOutputStream(conn.getOutputStream()); bytesAvailable = fin.available(); int thirdOfBytes = bytesAvailable / 3; int state = 0; bufferSize = Math.min(bytesAvailable, maxBufferSize); buffer = new byte[bufferSize]; bytesRead = fin.read(buffer, 0, bufferSize); while (bytesRead > 0) { dos.write(buffer, 0, bufferSize); bytesAvailable = fin.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); bytesRead = fin.read(buffer, 0, bufferSize); Log.i("VideoUpload", "->"); if (bytesAvailable < thirdOfBytes && state == 1) { publishProgress(4, i); state = 2; } else if (bytesAvailable < (2 * thirdOfBytes) && state == 0) { publishProgress(3, i); state = 1; } } publishProgress(5, i); serverResponseCode = conn.getResponseCode(); String serverResponseMessage = conn.getResponseMessage(); Log.i("VideoUpload", "HTTP Response is : " + serverResponseMessage + ": " + serverResponseCode); publishProgress(9, i); DataInputStream inStream; HashMap<String,String> responseMap = new HashMap<>(); try { inStream = new DataInputStream(conn.getInputStream()); String str; while ((str = inStream.readLine()) != null) { Log.e("VideoUpload", "SOF Server Response: " + str); String[] responseItem = str.split(" - "); responseMap.put(responseItem[0], responseItem[1]); } inStream.close(); if (responseMap.get("ERROR").equals("FALSE")) { int index2 = Integer.parseInt(responseMap.get("INDEX")); syncSucces2[index2] = true; Log.e("AddSucces(" + (addCount + 1) + "/" + (syncVideos.size()) + ")", video.toString()); } } catch (IOException e) { Log.e("VideoUpload", "SOF error: " + e.getMessage(), e); } fin.close(); dos.flush(); dos.close(); publishProgress(10, i); } catch (MalformedURLException ex) { ex.printStackTrace(); Log.e("VideoUpload", "UL error: " + ex.getMessage(), ex); } catch (Exception e) { e.printStackTrace(); Log.e("UploadFileException", "Exception : " + e.getMessage(), e); } } } } return null; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); progressBar.setProgress(values[0] + (values[1] * 10)); } @Override protected void onPreExecute() { super.onPreExecute(); progressBar = new ProgressDialog(PictureActivity.this); progressBar.setMax(10 * syncVideos.size()); progressBar.setMessage("Uploading Video File(s)"); progressBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressBar.setCancelable(false); progressBar.show(); } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); progressBar.hide(); refreshPhotos(); } } 

Y he cambiado mucho el PHP, en lugar de usar una parte de código en el archivo index.php (con la API REST) ​​hice otro archivo llamado uploadVideo.php:

 <?php require_once '../include/File_Streamer.php'; require_once '../include/DbHandler.php'; if (!isset($_SERVER['HTTP_X_FILE_NAME']) || !isset($_SERVER['HTTP_VID_ID']) || !isset($_SERVER['HTTP_VID_INDEX'])) { throw new Exception("Invalid Headers!"); } else { $im_path = $_SERVER['HTTP_X_FILE_NAME']; $id = $_SERVER['HTTP_VID_ID']; $index = $_SERVER['HTTP_VID_INDEX']; $db = new DbHandler(); $response = array(); if($db->updateVideo($id, $im_path)) { $im_path2 = explode("_",$im_path); $im_path2[0] .= 's'; $im_path2[2] = $im_path; $im_path3 = implode("/",$im_path2); $filepath = '../images/'.$im_path3; $dirpath = str_replace($im_path,"",$filepath); $ft = new File_Streamer(); $ft->setDestination(__DIR__ . '/' . $dirpath); if ($ft->receive()) { echo "ERROR - FALSE\n"; echo "MESSAGE - UPLOADED VIDEO WITH SUCCES\n"; echo "INDEX - " . $index; } else { echo "ERROR - TRUE\n"; echo "MESSAGE - FAILED TO SAVE VIDEO FILE"; } } else { echo "ERROR - TRUE\n"; echo "MESSAGE - FAILED TO ADD TO DATABASE"; } } 

Y estoy usando la siguiente clase también:

 <?php class File_Streamer { private $_fileName; private $_contentLength; private $_destination; public function __construct() { if (!isset($_SERVER['HTTP_X_FILE_NAME']) || !isset($_SERVER['CONTENT_LENGTH'])) { throw new Exception("Invalid Headers!"); } $this->_fileName = $_SERVER['HTTP_X_FILE_NAME']; $this->_contentLength = $_SERVER['CONTENT_LENGTH']; } public function isValid() { if (($this->_contentLength > 0)) { return true; } return false; } public function setDestination($destination) { $this->_destination = $destination; } public function receive() { try { if (!$this->isValid()) { throw new Exception('No file uploaded!'); } $fileReader = fopen('php://input', "r"); $fileWriter = fopen($this->_destination . $this->_fileName, "w+"); while(true) { $buffer = fgets($fileReader, 4096); if (strlen($buffer) == 0) { fclose($fileReader); fclose($fileWriter); return true; } fwrite($fileWriter, $buffer); } return false; } catch(Exception $ex) { echo "error: " . $ex->getMessage(); } } } 
  • android youtube: compartir un video de youtube a mi aplicación?
  • ¿Cómo detener el video de YouTube en la vista web de Android?
  • Android WebView - Los vídeos de Youtube que no se reproducen dentro de la aplicación
  • ¿Cómo aplicar el filtrado de video en android?
  • Cómo implementar el filtro de vídeo en Android como instagram
  • Compatibilidad con FFmpeg para descodificación de hardware libstagefright
  • Cómo seleccionar CBR vs VBR a través de la API de Android mientras graba vídeo en una aplicación
  • Reproducir videos de alta calidad de YouTube con VideoView
  • Crear video desde capturas de pantalla en android
  • ¿Cómo puedo reproducir vídeos de Youtube en un diseño personalizado de Android?
  • ¿Podemos volver a escribir la API de MediaCodec en C?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.