在安卓系统中以编程方式下载文件

35

我正在以编程方式从Web服务器下载文件。下载完成后,我检查了文件。大小、扩展名和所有其他参数都是正确的,但是当我尝试在媒体播放器中播放该文件时,它显示为已损坏。

这是我的代码:

    byte[] b = null;
    InputStream in = null;
    b = new byte[Integer.parseInt(size)];    // size of the file.
    in = OpenHttpConnection(URL);            
    in.read(b);
    in.close();

    File folder = new File("/sdcard", "folder");
   boolean check = folder.mkdirs();

   Log.d("HttpDownload", "check " + check);

   File myFile = new File("/sdcard/folder/" + name);


    myFile.createNewFile();
   OutputStream filoutputStream = new FileOutputStream(myFile);

   filoutputStream.write(b);

   filoutputStream.flush();

   filoutputStream.close();

请查看 https://dev59.com/9XNA5IYBdhLWcg3wh-cC#921400 - amrezzd
6个回答

55

这是我用于将给定URL下载到给定File对象的工作代码。 File对象(outputFile)仅使用new File(path)创建,我没有调用createNewFile或任何其他方法。

private static void downloadFile(String url, File outputFile) {
  try {
      URL u = new URL(url);
      URLConnection conn = u.openConnection();
      int contentLength = conn.getContentLength();

      DataInputStream stream = new DataInputStream(u.openStream());

        byte[] buffer = new byte[contentLength];
        stream.readFully(buffer);
        stream.close();

        DataOutputStream fos = new DataOutputStream(new FileOutputStream(outputFile));
        fos.write(buffer);
        fos.flush();
        fos.close();
  } catch(FileNotFoundException e) {
      return; // swallow a 404
  } catch (IOException e) {
      return; // swallow a 404
  }
}

能否从同一网站下载视频? - Rat-a-tat-a-tat Ratatouille
1
这是用于文件下载的。你可以下载视频文件,当然。但是如果你指的是流媒体视频,那么不,你需要使用不同的API。 - Eric Mill
3
我该如何创建可供传递的输出文件? - Vasant
1
请确保在另一个线程上运行此代码,而不会阻塞主线程。 - RamithDR

12

权限

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />

下载功能代码

 public void downloadFile() {
        String DownloadUrl = audio1;
        DownloadManager.Request request1 = new DownloadManager.Request(Uri.parse(DownloadUrl));
        request1.setDescription("Sample Music File");   //appears the same in Notification bar while downloading
        request1.setTitle("File1.mp3");
        request1.setVisibleInDownloadsUi(false);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            request1.allowScanningByMediaScanner();
            request1.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
        }
        request1.setDestinationInExternalFilesDir(getApplicationContext(), "/File", "Question1.mp3");

        DownloadManager manager1 = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
        Objects.requireNonNull(manager1).enqueue(request1);
        if (DownloadManager.STATUS_SUCCESSFUL == 8) {
        DownloadSuccess(); 
        }
    }

这是下载的实际和最佳解决方案。使用此方法,您的文件将自动由Android系统处理。这个解决方案拯救了我的整个项目。 - Anand Savjani
看起来是一个可靠的解决方案,但是请问它能够下载多大的尺寸/字节数? - karan_for_you

5
private void down(String string)
    {

        try
        {
            URL url = new URL(URL);
            HttpURLConnection c = (HttpURLConnection) url.openConnection();
            c.setRequestMethod("GET");
            c.setDoOutput(true);
            c.connect();

            String PATH = Environment.getExternalStorageDirectory().toString()
                    + "/load";
            Log.v("LOG_TAG", "PATH: " + PATH);

            File file = new File(PATH);
            file.mkdirs();
            File outputFile = new File(file, option14[i].toString());
            FileOutputStream fos = new FileOutputStream(outputFile);
            InputStream is = c.getInputStream();

            byte[] buffer = new byte[4096];
            int len1 = 0;

            while ((len1 = is.read(buffer)) != -1)
            {
                fos.write(buffer, 0, len1);
            }

            fos.close();
            is.close();

            Toast.makeText(this, " A new file is downloaded successfully",
                    Toast.LENGTH_LONG).show();

        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

    }

这对我很有效!谢谢。但是你能解释一下这行代码吗:byte[] buffer = new byte[4096];?它是文件允许的最大字节数吗? - infinite_loop_

2

2
1> 为Handel下载过程创建一个接口
interface Downloader { fun downloadFile(url:String,fileSubPath:String):Long }

创建一个用于处理下载管理的类(在我的情况下,我创建了一个名为AndroidDownloader的类),并按照以下步骤进行操作。
class AndroidDownloader(context: Context):Downloader {

private val downloadManager = context.getSystemService(DownloadManager::class.java)
override fun downloadFile(url: String,fileSubPath:String): Long {
    val request = DownloadManager.Request(url.toUri())
        .setMimeType("*/*")
        .setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI)
        .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
        .setTitle("Downloading")
        .addRequestHeader("Authorization", "Bearer <token>") //Add this if token required for download file
        .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileSubPath)
    return downloadManager.enqueue(request)
} }

3> 现在创建一个 BroadCastReciver 类来监控文件下载过程。(在我的情况下,我创建了一个 DownloadCompleteReceiver 类)
class DownloadCompleteReceiver:BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
    if(intent?.action == "android.intent.action.DOWNLOAD_COMPLETE") {
        val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1L)
        if(id != -1L) {
            println("Download with ID $id finished!")
            context?.toast(context.resources.getString(R.string.text_file_download_successfully))
        }
    }
} }

4> 现在在 AndroidManifest.xml 中注册您的 BroadcastReceiver
        <receiver
        android:name=".utils.DownloadCompleteReceiver"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
        </intent-filter>
    </receiver>

5> 最后的步骤,在您想要下载文件的活动或片段中添加以下行。
val downloader=AndroidDownloader(context)
    downloader.downloadFile(url,"total${Date().time}.$excitationPath")

1

从输入流中读取并不保证文件的全部内容一次性被拉下来。请检查in.read(b)这一行的返回值。它可能长这样:

if(in.read(b) != size)
    Log.e("Network","Failed to read all data!");

这将至少告诉您是否从网络层获取了所有数据。如果您只收到了部分读取,但仍在将完整的字节数组写入磁盘,则可能会解释为什么媒体播放器认为文件已损坏。


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