如何在 Android 中的服务器上发布大型视频?

Posted

技术标签:

【中文标题】如何在 Android 中的服务器上发布大型视频?【英文标题】:How to post large video on a server, in Android? 【发布时间】:2013-06-03 09:03:13 【问题描述】:

我正在尝试发布一个大型视频(近 1 GB)。

我正在使用 FTP 将视频发送到服务器,但上传会在一段时间后停止。服务器上的视频崩溃了,但我可以上传较小尺寸的视频。

我还使用 HTTP 将视频发送到服务器,作为 Base64 编码字符串发送,但编码时出现内存不足异常。

我尝试将视频作为文件上传,但没有成功。将大型视频上传到服务器的最佳方式是什么?

【问题讨论】:

【参考方案1】:

使用 HTTP POST,并将内容发布为 Form-based File Upload(mime 类型:multipart/form-data)。该系统是用于发送表单和/或上传文件的网络标准。

使用 HTTP 分块发布模式,这样就不需要事先知道大小,您可以将任何文件分成小部分进行流式传输。您仍然需要在服务器上编写一些代码(例如在 php 中)以接受文件并执行所需的操作。

使用 HttpURLConnection 发起连接。然后使用我的附加类发送数据。它将创建适当的标头等,您将使用它作为 OutputStream 将原始数据写入其中,然后调用 close,您就完成了。您可以覆盖其 onHandleResult 以处理生成的错误代码。

public class FormDataWriter extends FilterOutputStream
   private final HttpURLConnection con;

   /**
    * @param formName name of form in which data are sent
    * @param fileName
    * @param fileSize   size of file, or -1 to use chunked encoding
    */
   FormDataWriter(HttpURLConnection con, String formName, String fileName, long fileSize) throws IOException
      super(null);
      this.con = con;
      con.setDoOutput(true);

      String boundary = generateBoundary();
      con.setRequestProperty(HTTP.CONTENT_TYPE, "multipart/form-data; charset=UTF-8; boundary="+boundary);
      
         StringBuilder sb = new StringBuilder();
         writePartHeader(boundary, formName, fileName==null ? null : "filename=\""+fileName+"\"",
               "application/octet-stream", sb);
         headerBytes = sb.toString().getBytes("UTF-8");

         sb = new StringBuilder();
         sb.append("\r\n");
         sb.append("--"+boundary+"--\r\n");
         footerBytes = sb.toString().getBytes();
      
      if(fileSize!=-1) 
         fileSize += headerBytes.length + footerBytes.length;
         con.setFixedLengthStreamingMode((int)fileSize);
      else
         con.setChunkedStreamingMode(0x4000);
      out = con.getOutputStream();
   

   private byte[] headerBytes, footerBytes;

   private String generateBoundary() 
      StringBuilder sb = new StringBuilder();
      Random rand = new Random();
      int count = rand.nextInt(11) + 30;
      int N = 10+26+26;
      for(int i=0; i<count; i++) 
         int r = rand.nextInt(N);
         sb.append((char)(r<10 ? '0'+r : r<36 ? 'a'+r-10 : 'A'+r-36));
      
      return sb.toString();
   

   private void writePartHeader(String boundary, String name, String extraContentDispositions, String contentType, StringBuilder sb) 
      sb.append("--"+boundary+"\r\n");
      sb.append("Content-Disposition: form-data; charset=UTF-8; name=\""+name+"\"");
      if(extraContentDispositions!=null)
         sb.append("; ").append(extraContentDispositions);
      sb.append("\r\n");
      if(contentType!=null)
         sb.append("Content-Type: "+contentType+"\r\n");
      sb.append("\r\n");
   

   @Override
   public void write(byte[] buffer, int offset, int length) throws IOException
      if(headerBytes!=null) 
         out.write(headerBytes);
         headerBytes = null;
      
      out.write(buffer, offset, length);
   

   @Override
   public void close() throws IOException
      flush();
      if(footerBytes!=null) 
         out.write(footerBytes);
         footerBytes = null;
      
      super.close();
      int code = con.getResponseCode();
      onHandleResult(code);
   
   protected void onHandleResult(int code) throws IOException
      if(code!=200 && code!=201)
         throw new IOException("Upload error code: "+code);
   

【讨论】:

什么是formName?是用于创建上传xml或json的服务器表单的formName吗? 对于 PHP 端,请参阅 php.net/manual/en/features.file-upload.post-method.php。 formName 是您将在 $_FILES['name'] 中在 PHP 中使用的变量的名称。 是否还取决于服务器最大上传大小和执行时间? 帮帮我上传文件不成功。 只按文件大小,是可以的。【参考方案2】:

我猜它失败是因为大尺寸超时。

自从

小尺寸视频上传成功

,我的建议是

    将一个大文件拆分为多个小文件。 根据网络情况,一张一张或多张一起上传。 在服务器上加入所有部分(在所有成功上传之后)。 由于体积小,重新上传失败的部分很容易。

只是一个理论。

This site 可能会有所帮助。


添加 08.01.2013

已经有一段时间了,不知道你是否还需要这个。无论如何,我写了一些简单的代码来实现上面的理论,主要是因为兴趣。

    把一个大文件拆分成几个小文件。把大文件读成几个小部分。

    ByteBuffer bb = ByteBuffer.allocate(partSize);
    int bytesRead = fc.read(bb);
    if (bytesRead == -1) 
        break;
    
    byte[] bytes = bb.array();
    parts.put(new Part(createFileName(fileName, i), bytes));
    

    根据网络情况,一张一张或多张一起上传。

    Part part = parts.take();
    if (part == Part.NULL) 
        parts.add(Part.NULL);// notify others to stop.
        break;
     else 
        uploader.upload(part);
    
    

    加入服务器的所有部分(在所有成功上传之后)。 因为是通过HTTP,所以可以是任何语言,如Java、PHP、Python等。这里是一个java的例子。

    ...
    try (FileOutputStream dest = new FileOutputStream(destFile, true)) 
    
        FileChannel dc = dest.getChannel();// the final big file.
        for (long i = 0; i < count; i++) 
            File partFile = new File(destFileName + "." + i);// every small parts.
            if (!partFile.exists()) 
                break;
            
            try (FileInputStream part = new FileInputStream(partFile)) 
                FileChannel pc = part.getChannel();
                pc.transferTo(0, pc.size(), dc);// combine.
            
            partFile.delete();
        
        statusCode = OK;// set ok at last.
     catch (Exception e) 
        log.error("combine failed.", e);
    
    

我把所有代码都放在GitHub。还发了android example。

如果你还需要,请看一下。

【讨论】:

如何在我的项目中实现它?【参考方案3】:

私有 HttpsURLConnection conn = null;

conn.setDoInput(true);

conn.setDoOutput(true);

conn.setUseCaches(false);

conn.setChunkedStreamingMode(1024);

【讨论】:

以上是关于如何在 Android 中的服务器上发布大型视频?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android 应用程序的登录页面(背景)上播放视频文件?

如何使用适用于 Android 的 OpenCV 减少实时视频序列中的运动效果?

如何在 mp4 视频文件中添加 MOOV 原子

如何在我的 Android 应用程序中集成 Quick Blox 视频聊天服务?

如何从Android中的图像数组创建视频?

无法在android的videoview中播放来自url的视频如何在videoview中播放来自URL的视频?