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() {

                @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() {

                @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 文件上传)