android | 文件下载

互联网的世界,文件的下载和上传是最常见的操作。当然,涉及到下载文件的协议有很多,如FTP、SFTP、HTTP等,这里就简单使用HTTP协议来下载文件。

而还获取网络的文件数据,我们就需要获取网络的链接(连接)。百度了一下,可以用的类比较多,这里就使用HttpURLConnection来操作。如下摘抄一下:
使用这个类遵循以下的规则:

  1. 使用URL#openConnection()来得到连接对象HttpURLConnection
  2. 数据请求头部可以设置URI、登录凭证、首选数据类型、SessionCookies
  3. 上传数据实例如果包含请求体就必须配置 setDoOutput(true)
  4. 读响应的数据,响应的头部通常包含响应头如响应正文的内容类型、长度、修改日期以及Cookie,响应体。如果没有正文,该流为空。
  5. 一旦读取了响应流数据,就应该调用disconnect()来关闭流,释放资源。

在文档中提供了一个简单的案例:

URL url = new URL("http://www.android.com/");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
    InputStream in = new BufferedInputStream(urlConnection.getInputStream());
    readStream(in);
  } finally {
    urlConnection.disconnect();
}

有网页开发的都知道,HTTP协议是有状态码的,这里也有(该类的属性):
状态码很多,这里就只谈200

HTTP_OKHTTP状态代码200:好的。

接下来就看看方法(包括从URLConnection中继承的):【摘取部分】

StringgetRequestMethod()获取请求方法
IntgetResponseCode()获取状态代码
UnitsetRequestMethod(method: String!)设置URL请求的方法,GET|POST|HEAD|OPTIONS|PUT|DELETE|TRACE
IntgetConnectTimeout()返回连接超时的设置。0为无限超时。
UnitsetConnectTimeout(timeout: Int)设置在打开与此URLConnection引用的资源的通信链接时要使用的指定超时值(以毫秒为单位)。如果超时在可以建立连接之前到期,则引发java.net.SocketTimeoutException。超时为零被解释为无限超时。
StringgetContentEncoding()返回content-encoding标头字段的值。
IntgetContentLength()返回content-length标头字段的值。
LonggetContentLengthLong()content-length以long形式返回标头字段的值。
StringgetContentType()返回content-type标头字段的值。
LonggetDate()返回date标头字段的值。
InputStreamgetInputStream()返回从此打开的连接读取的输入流。如果读取超时在数据可用于读取之前到期,则在从返回的输入流读取时可以抛出SocketTimeoutException。
LonggetLastModified()返回last-modified标头字段的值。结果是格林威治标准时间1970年1月1日以来的毫秒数。
OutputStreamgetOutputStream()返回写入此连接的输出流。
IntgetReadTimeout()返回读取超时的设置。0为无限超时。
URLgetURL()返回此值URLConnection的URL字段。
StringtoString()返回此值URLConnection的URL字段的字符串形式。

下面就开始图片下载的案例:
图片地址:https://pic.cnblogs.com/avatar/1142647/20170416093225.png
这里就不写如果在ImageView中设置图片的了。在下一篇博客中写明了。这里就简单使用,然后下载下来。
所以这里也就不需要关注ImageView中是如何设置图片的,要不然,其实在设置图片的时候已经可以保存了,这里再继续用按钮点击事件去访问图片就多余了。这里就关注按钮的事件。

获取当前设备SD卡的目录:

Environment.getExternalStorageDirectory()

还是在adb shell中,在/mnt目录下,查看一下sdcard的权限,如图:

也就是只读的,我们需要修改一下:
运行命令:mount -o remount rw /
然后用chmod命令修改: chmod 777 sdcard 我这里为了方便,赋予了最高的读写权限
照例,还是查看一下:

然后,编程测试发现还是解决不了,这里就不做深入探究了。果断弃之。然后就用下面的目录,将文件还是放置到本应用下:

File file = new File("data/data/com.weizu.intent", filename);
//File file  = new File(Environment.getExternalStorageDirectory(), filename); //抛弃

布局文件是ImageView+Button,着重点也就是在Button的事件监听函数中:
至于ImageView中是如何设置网站的链接图片的,就参考下一讲吧。
这里就给出MainActivity.java,还是一样的访问网络的操作放置到子线程中,然后保存文件就在主线程中完成。使用Message对象传递消息的时候,不能直接设置消息是obj,要不然会报主线程中不能访问网络的错误,也就是在HttpURLConnection得到的InputStream是能代表网络的连接对象的,需要包装一下,这里如何包装就放置到了下下讲,看代码:

package com.weizu.intent;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity{
    private MyImageView img;
    private Button btn;
    private final String path = "http://pics.sc.chinaz.com/files/pic/pic9/201811/bpic9300.jpg";
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(msg.what == 1){
                Bitmap bp = (Bitmap)msg.obj;
                //转换
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                bp.compress(Bitmap.CompressFormat.PNG, 100, baos);
                InputStream isBm = new ByteArrayInputStream(baos.toByteArray());
                saveFile("weizu.png", isBm);
            }else{
                Toast.makeText(MainActivity.this, "获取失败", 20).show();
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        img = (MyImageView)findViewById(R.id.image);
        img.setImageURL(path);
        btn = (Button)findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //子线程中完成联网获取数据的操作。
                new Thread(new Runnable(){
                    @Override
                    public void run() {
                        try {
                            URL url = new URL(path);
                            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
                            connection.setRequestMethod("GET");
                            if(connection.getResponseCode()==HttpURLConnection.HTTP_OK){
                                InputStream in = connection.getInputStream();
                                Bitmap bp = BitmapFactory.decodeStream(in);
                                //发送消息给主线程,以更新UI
                                Message message = Message.obtain();
                                message.what = 1;
                                message.obj = bp;
                                handler.sendMessage(message);
                            }
                        } catch (Exception e) {
                            handler.sendEmptyMessage(2);
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        });
    }
    public void saveFile(String filename, InputStream in){
        FileOutputStream fos = null;
        try {
            File file = new File("data/data/com.weizu.intent", filename);
            fos=new FileOutputStream(file);
            byte buffer[] = new byte[4 * 1024];
            while((in.read(buffer)) != -1){
                fos.write(buffer);
            }
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                in.close();
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

存储权限和联网权限:

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>

效果,直接在Eclipse中用DBMS->File Explorer->data/data/com.weizu.intent目录下就可以看见weizu.png:


   Reprint policy


《android | 文件下载》 by 无涯明月 is licensed under a Creative Commons Attribution 4.0 International License
 Previous
android | ImageView加载网络上图片 android | ImageView加载网络上图片
百度了一下,发现有很多种实现方式,但是大致的思路都是一样的。地址一 地址二 不使用Handler,通过按钮更新UI视图敲敲打打,就开始写程序。然后发现程序崩溃!是的,然后又开始了百度之路。 解决思路划重点:在android4.0 之
2019-08-19
Next 
android | SQLite数据库操作 android | SQLite数据库操作
得到了数据库的对象,接着就是对数据库进行操作。增删改查。我们就需要查看SQLiteDatabase 数据库操作的一些方法下面就是我从文档中摘取的一些方法: 返回值方法说明 voidexecSQL(String sql)Execute
2019-08-18
  TOC