Android 文件上传

android 文件上传主要有两种方式,HttpUrlConnection上传和Socket上传,
下面贴出实现代码:

public class HttpUploadFileHelper {
    private final static String TAG = HttpUploadFileHelper.class.getSimpleName();

    /**
     * http 请求消息体中的上传文件边界标识
     */
    private static final String BOUNDARY = UUID.randomUUID().toString();
    /**
     * 文件类型
     */
    private static final String CONTENT_TYPE = "multipart/form-data";

    private static final String PREFIX = "--";

    /**
     * http 请求消息体中的回车换行
     */
    private static final String CRLF = "\r\n";

    private static final String CHARSET_UTF_8 = "UTF-8";
    /**
     * 表单名
     */
    private static final String FORM_NAME = "upload_file";


    private HttpUploadFileHelper() {

    }

    /**
     * 使用HttpUrlConnection来向服务器上传文件,在上传大文件时,会造成内存溢出
     *
     * @param url
     * @param filePath
     * @param listener
     */
    public static void sendByHttpUrlConnection(final String url, final String filePath, final UploadResultListener listener) {
        if (TextUtils.isEmpty(url) || TextUtils.isEmpty(filePath)) {//校验上传路径和文件
            return;
        }

        final File uploadFile = new File(filePath);
        if (uploadFile.exists() && uploadFile.isFile()) {
            new AsyncTask<Void, Void, Boolean>() {

                @Override
                protected Boolean doInBackground(Void... params) {

                    try {
                        StringBuffer headBuffer = new StringBuffer(); //构建文件头部信息
                        headBuffer.append(PREFIX);
                        headBuffer.append(BOUNDARY);
                        headBuffer.append(CRLF);
                        headBuffer.append("Content-Disposition: form-data; name=\"" + FORM_NAME + "\"; filename=\"" + uploadFile.getName() + "\"" + CRLF);//模仿web上传文件提交一个form表单给服务器,表单名随意起
                        headBuffer.append("Content-Type: application/octet-stream" + CRLF);//若服务器端有文件类型的校验,必须明确指定Content-Type类型
                        headBuffer.append(CRLF);
                        Log.i(TAG, headBuffer.toString());
                        byte[] headBytes = headBuffer.toString().getBytes();

                        StringBuffer endBuffer = new StringBuffer();//构建文件结束行
                        endBuffer.append(CRLF);
                        endBuffer.append(PREFIX);
                        endBuffer.append(BOUNDARY);
                        endBuffer.append(PREFIX);
                        endBuffer.append(CRLF);
                        byte[] endBytes = endBuffer.toString().getBytes();

                        URL remoteUrl = new URL(url);
                        HttpURLConnection httpURLConnection = (HttpURLConnection) remoteUrl.openConnection();
                        httpURLConnection.setDoOutput(true);//打开输出流
                        httpURLConnection.setDoInput(true);//打开输入流
                        httpURLConnection.setUseCaches(false);
                        httpURLConnection.setRequestMethod("POST");//上传文件必须要POST请求
                        httpURLConnection.setRequestProperty("Charset", CHARSET_UTF_8);//设置编码
                        httpURLConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);//设置http消息头部的Content-Type
                        String contentLength = String.valueOf(headBytes.length + endBytes.length + uploadFile.length());
                        httpURLConnection.setRequestProperty("Content-Length", contentLength);//设置内容长度

                        OutputStream outputStream = httpURLConnection.getOutputStream();
                        outputStream.write(headBytes);//输出文件头部

                        FileInputStream fileInputStream = new FileInputStream(uploadFile);
                        byte[] buffer = new byte[1024];
                        int length;
                        while ((length = fileInputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, length);//输出文件内容
                        }
                        fileInputStream.close();

                        outputStream.write(endBytes);//输出结束行
                        outputStream.close();

                        if (httpURLConnection.getResponseCode() == 200) {//发送成功
                            return true;
                        } else {
                            return false;
                        }

                    } catch (MalformedURLException e) {
                        e.printStackTrace();
                        return false;
                    } catch (IOException e) {
                        e.printStackTrace();
                        return false;
                    }
                }

                @Override
                protected void onPostExecute(Boolean result) {
                    super.onPostExecute(result);
                    if (listener != null) {
                        if (result) {
                            listener.onSuccess();
                        } else {
                            listener.onFailure();
                        }
                    }
                }

            }.execute();

        }
    }

    /**
     * 使用Socket向服务器上传文件,上传大文件时建议使用Socket,才不会造成内存溢出
     *
     * @param url
     * @param filePath
     * @param listener
     */
    public static void sendBySocket(final String url, String filePath, final UploadResultListener listener) {
        if (TextUtils.isEmpty(url) || TextUtils.isEmpty(filePath)) {
            return;
        }

        final File uploadFile = new File(filePath);
        if (uploadFile.exists() && uploadFile.isFile()) {
            new AsyncTask<Void, Void, Boolean>() {

                @Override
                protected Boolean doInBackground(Void... params) {
                    try {
                        StringBuffer headBuffer = new StringBuffer(); //构建文件头部信息
                        headBuffer.append(PREFIX);
                        headBuffer.append(BOUNDARY);
                        headBuffer.append(CRLF);
                        headBuffer.append("Content-Disposition: form-data; name=\"" + FORM_NAME + "\"; filename=\"" + uploadFile.getName() + "\"" + CRLF);//模仿web上传文件提交一个form表单给服务器,表单名随意起
                        headBuffer.append("Content-Type: application/octet-stream" + CRLF);//若服务器端有文件类型的校验,必须明确指定Content-Type类型
                        headBuffer.append(CRLF);
                        Log.i(TAG, headBuffer.toString());
                        byte[] headBytes = headBuffer.toString().getBytes();

                        StringBuffer endBuffer = new StringBuffer();//构建文件结束行
                        endBuffer.append(CRLF);
                        endBuffer.append(PREFIX);
                        endBuffer.append(BOUNDARY);
                        endBuffer.append(PREFIX);
                        endBuffer.append(CRLF);
                        byte[] endBytes = endBuffer.toString().getBytes();

                        URL remoteUrl = new URL(url);
                        Socket socket = new Socket(remoteUrl.getHost(), remoteUrl.getPort());
                        OutputStream outputStream = socket.getOutputStream();
                        PrintStream printStream = new PrintStream(outputStream, true, CHARSET_UTF_8);

                        //输出请求头,用println输出可以省了后面的换行
                        printStream.println("POST " + url + " HTTP/1.1");
                        printStream.println("Content-Type: multipart/form-data; boundary=" + BOUNDARY);
                        String contentLength = String.valueOf(headBytes.length + endBytes.length + uploadFile.length());
                        printStream.println("Content-Length: " + contentLength);
                        printStream.println();//根据 HTTP 协议,空行将结束头信息

                        outputStream.write(headBytes);//输出文件头部

                        FileInputStream fileInputStream = new FileInputStream(uploadFile);
                        byte[] buffer = new byte[1024];
                        int length;
                        while ((length = fileInputStream.read(buffer)) != -1) {//输出文件内容
                            outputStream.write(buffer, 0, length);
                        }
                        fileInputStream.close();

                        outputStream.write(endBytes);//输出结束行
                        outputStream.close();

                        return true;

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

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

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

                @Override
                protected void onPostExecute(Boolean result) {
                    super.onPostExecute(result);
                    if (listener != null) {
                        if (result) {
                            listener.onSuccess();

                        } else {
                            listener.onFailure();

                        }
                    }
                }

            }.execute();
        }

    }

    /**
     * 监听上传结果
     */
    public static interface UploadResultListener {

        /**
         * 上传成功
         */
        public void onSuccess();

        /**
         * 上传失败
         */
        public void onFailure();
    }
}

对于文件上传需要注意的是,如果是大文件就要采用Socket上传,以免发生OOM,而大文件的上传也可以实现为断点上传,需要服务端配合实现,和断点下载实现类似,也采用RandomAccessFile来做文件移动,具体的断点上传实现代码如下:

客户端:

 /**  
     * 上传文件  
     * @param uploadFile  
     */    
    private void uploadFile(final File uploadFile) {    
        new Thread(new Runnable() {             
            @Override    
            public void run() {    
                try {    
                    uploadbar.setMax((int)uploadFile.length());    
                    String souceid = logService.getBindId(uploadFile);    
                    String head = "Content-Length="+ uploadFile.length() + ";filename="+ uploadFile.getName() + ";sourceid="+    
                        (souceid==null? "" : souceid)+"\r\n";    
                    Socket socket = new Socket("192.168.1.78",7878);    
                    OutputStream outStream = socket.getOutputStream();    
                    outStream.write(head.getBytes());    
                        
                    PushbackInputStream inStream = new PushbackInputStream(socket.getInputStream());        
                    String response = StreamTool.readLine(inStream);    
                    String[] items = response.split(";");    
                    String responseid = items[0].substring(items[0].indexOf("=")+1);    
                    String position = items[1].substring(items[1].indexOf("=")+1);    
                    if(souceid==null){//代表原来没有上传过此文件,往数据库添加一条绑定记录    
                        logService.save(responseid, uploadFile);    
                    }    
                    RandomAccessFile fileOutStream = new RandomAccessFile(uploadFile, "r");    
                    fileOutStream.seek(Integer.valueOf(position));    
                    byte[] buffer = new byte[1024];    
                    int len = -1;    
                    int length = Integer.valueOf(position);    
                    while(start&&(len = fileOutStream.read(buffer)) != -1){    
                        outStream.write(buffer, 0, len);    
                        length += len;    
                        Message msg = new Message();    
                        msg.getData().putInt("size", length);    
                        handler.sendMessage(msg);    
                    }    
                    fileOutStream.close();    
                    outStream.close();    
                    inStream.close();    
                    socket.close();    
                    if(length==uploadFile.length()) logService.delete(uploadFile);    
                } catch (Exception e) {    
                    e.printStackTrace();    
                }    
            }    
        }).start();    
    }    
}    

服务端代码如下:

public SocketServer(int port) {  
        this.port = port;  
        // 初始化线程池  
        executorService = Executors.newFixedThreadPool(Runtime.getRuntime()  
                .availableProcessors() * 50);  
    }  
  
    // 启动服务  
    public void start() throws Exception {  
        ss = new ServerSocket(port);  
        while (!quit) {  
            Socket socket = ss.accept();// 接受客户端的请求  
            // 为支持多用户并发访问,采用线程池管理每一个用户的连接请求  
            executorService.execute(new SocketTask(socket));// 启动一个线程来处理请求  
        }  
    }  
  
    // 退出  
    public void quit() {  
        this.quit = true;  
        try {  
            ss.close();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
  
    public static void main(String[] args) throws Exception {  
        SocketServer server = new SocketServer(7878);  
        server.start();  
    }  
  
    private class SocketTask implements Runnable {  
        private Socket socket;  
  
        public SocketTask(Socket socket) {  
            this.socket = socket;  
        }  
  
        @Override  
        public void run() {  
            try {  
                System.out.println("accepted connenction from "  
                        + socket.getInetAddress() + " @ " + socket.getPort());  
                PushbackInputStream inStream = new PushbackInputStream(  
                        socket.getInputStream());  
                // 得到客户端发来的第一行协议数据:Content-Length=143253434;filename=xxx.3gp;sourceid=  
                // 如果用户初次上传文件,sourceid的值为空。  
                String head = StreamTool.readLine(inStream);  
                System.out.println(head);  
                if (head != null) {  
                    // 下面从协议数据中读取各种参数值  
                    String[] items = head.split(";");  
                    String filelength = items[0].substring(items[0].indexOf("=") + 1);  
                    String filename = items[1].substring(items[1].indexOf("=") + 1);  
                    String sourceid = items[2].substring(items[2].indexOf("=") + 1);  
                    Long id = System.currentTimeMillis();  
                    FileLog log = null;  
                    if (null != sourceid && !"".equals(sourceid)) {  
                        id = Long.valueOf(sourceid);  
                        log = find(id);//查找上传的文件是否存在上传记录  
                    }  
                    File file = null;  
                    int position = 0;  
                    if(log==null){//如果上传的文件不存在上传记录,为文件添加跟踪记录  
                        String path = new SimpleDateFormat("yyyy/MM/dd/HH/mm").format(new Date());  
                        File dir = new File(uploadPath+ path);  
                        if(!dir.exists()) dir.mkdirs();  
                        file = new File(dir, filename);  
                        if(file.exists()){//如果上传的文件发生重名,然后进行改名  
                            filename = filename.substring(0, filename.indexOf(".")-1)+ dir.listFiles().length+ filename.substring(filename.indexOf("."));  
                            file = new File(dir, filename);  
                        }  
                        save(id, file);  
                    }else{// 如果上传的文件存在上传记录,读取上次的断点位置  
                        file = new File(log.getPath());//从上传记录中得到文件的路径  
                        if(file.exists()){  
                            File logFile = new File(file.getParentFile(), file.getName()+".log");  
                            if(logFile.exists()){  
                                Properties properties = new Properties();  
                                properties.load(new FileInputStream(logFile));  
                                position = Integer.valueOf(properties.getProperty("length"));//读取断点位置  
                            }  
                        }  
                    }  
                      
                    OutputStream outStream = socket.getOutputStream();  
                    String response = "sourceid="+ id+ ";position="+ position+ "\r\n";  
                    //服务器收到客户端的请求信息后,给客户端返回响应信息:sourceid=1274773833264;position=0  
                    //sourceid由服务生成,唯一标识上传的文件,position指示客户端从文件的什么位置开始上传  
                    outStream.write(response.getBytes());  
                      
                    RandomAccessFile fileOutStream = new RandomAccessFile(file, "rwd");  
                    if(position==0) fileOutStream.setLength(Integer.valueOf(filelength));//设置文件长度  
                    fileOutStream.seek(position);//移动文件指定的位置开始写入数据  
                    byte[] buffer = new byte[1024];  
                    int len = -1;  
                    int length = position;  
                    while( (len=inStream.read(buffer)) != -1){//从输入流中读取数据写入到文件中  
                        fileOutStream.write(buffer, 0, len);  
                        length += len;  
                        Properties properties = new Properties();  
                        properties.put("length", String.valueOf(length));  
                        FileOutputStream logFile = new FileOutputStream(new File(file.getParentFile(), file.getName()+".log"));  
                        properties.store(logFile, null);//实时记录文件的最后保存位置  
                        logFile.close();  
                    }  
                    if(length==fileOutStream.length()) delete(id);  
                    fileOutStream.close();                    
                    inStream.close();  
                    outStream.close();  
                    file = null;  
                }  
            } catch (Exception e) {  
                e.printStackTrace();  
            } finally {  
                try {  
                    if(socket != null && !socket.isClosed()) socket.close();  
                } catch (IOException e) {}  
            }  
        }  
  
    }  

最后是目前使用的OKHTTP上传文件代码:

//通过“addFormDataPart”可以添加多个上传的文件。
public  class OkHttpCallBackWrap {
    public void post(String url) throws IOException{
                File file = new File("D:/app/dgm/3.mp4");
                RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
                RequestBody requestBody = new MultipartBody.Builder()
                        .setType(MultipartBody.FORM) 
                        .addFormDataPart("application/octet-stream", "1.mp4", fileBody)
                        .build();
                Request request = new Request.Builder()
                        .url(url)
                        .post(requestBody)
                        .build();

                final okhttp3.OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
                OkHttpClient okHttpClient  = httpBuilder
                        //设置超时
                        .connectTimeout(100, TimeUnit.SECONDS)
                        .writeTimeout(150, TimeUnit.SECONDS)
                        .build();
                okHttpClient.newCall(request).enqueue(new Callback() {

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        System.out.println(response.body().string());
                    }

                    @Override
                    public void onFailure(Call arg0, IOException e) {
                        // TODO Auto-generated method stub
                        System.out.println(e.toString());

                    }

                });
            }
        }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,799评论 25 707
  • 文件上传与下载 文件上传 -- 服务端 以Tomcat为服务器,Android客服端访问Servlet,经Serv...
    sunhaiyu阅读 14,147评论 2 20
  • 文件上传在B/S应用中是一种十分常见的功能,那么在Android平台下是否可以实现像B/S那样的文件上传功能呢?答...
    0dce86ba3565阅读 510评论 0 1
  • 上传的方式 本文将介绍2中文件上传的方式:1.multipart/from-data方式上传。2.binary方式...
    blueberry_mu阅读 3,916评论 0 4
  • 翻过山的人会对没有翻过山的人说,山后面不过如此。但终究还是有很多人从没有翻过这座山。 山顶上“不过如此的”心情,你...
    黄小田阅读 310评论 0 2