Android 文件上传

作者: SDY_0656 | 来源:发表于2017-09-27 16:42 被阅读172次

    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());
    
                        }
    
                    });
                }
            }
    

    相关文章

      网友评论

        本文标题:Android 文件上传

        本文链接:https://www.haomeiwen.com/subject/zybuextx.html