Socket Large File Breakpoint Upload in Android

Keywords: Android Java socket network

What is Socket?

 Socket s are also called sockets. They are used to describe IP addresses and ports. They are handles for communication links. Applications usually send requests to or respond to network requests through sockets. They are abstract representations of endpoints in the process of network communication. It mainly includes the following two protocols:

  TCP (Transmission Control Protocol): Transmission Control Protocol, which provides connection-oriented, reliable byte stream services. Before clients and servers exchange data with each other, a TCP connection must be established between them before data can be transmitted. TCP provides functions such as overtime retransmitting, discarding duplicate data, checking data, flow control, etc. to ensure that data can be transmitted from one end to the other.
  UDP (User Datagram Protocl User Datagram Protocol): User Datagram Protocol is a simple transport layer protocol for datagrams. UDP does not provide reliability, it just sends data packets from the application to the IP layer, but it does not guarantee that they can reach their destination. Because UDP does not need to establish a connection between the client and the server before transferring the datagram, and there is no mechanism such as timeout and retransmit, the transmission speed of UDP is very fast.

Detailed explanations are as follows:

  TCP transmission is different from UDP. TCP transmission is streaming. First, the connection must be established, and then the data stream is transmitted along the connected line (virtual circuit). Therefore, TCP data flow will not be like UDP datagrams, each datagram will contain the destination address and port, because each datagram will be routed separately. TCP transmission only needs to specify the destination address and port when establishing the connection.

In image, TCP is like making a phone call, UDP is like sending a telegram. Generally speaking, UDP does not distinguish between client and server. Both sides of the communication are equal. Microscopically speaking, there is only one message, the sender is the client, and the listener is the server. Sender sending data to router is like sending a telegram to post office. The latter thing is that sender can't control and know nothing about it. So it's unreliable. There may be missing messages and no one knows about them. Just as every telegram needs a recipient, every datagram needs a destination address and port.

TCP is divided into client and server for every connection. The initiator of the connection (comparable to the dial-up caller) is the client, and the listener (equivalent to the person waiting to answer the phone) is the server. The initiator specifies the address and port of the server to be connected (equivalent to dialing), and the listener establishes the connection by shaking hands with the initiator three times (equivalent to answering the phone when the phone rings). After establishing the connection, both sides can send and receive data (call) from each other.

How does Java operate Socket?

   It is worth mentioning that Java provides corresponding classes for TCP and UDP respectively. TCP is a kind of socket and Server Socket in java. NET, which are used to represent the client and server of two-way connection respectively. These are two very well encapsulated classes. They are very convenient to use. UDP is java.Net.DatagramSocket.

   127.0.0.1 is the loop address, which is used for testing. It is equivalent to the local host local address. It has no network card and can be accessed without DNS. The port address is between 0 and 65535. The port between 0 and 1023 is used for some well-known network services and applications. The user's ordinary network application should use more than 1024 ports.

The Socket communication model is as follows:

If you still have ambiguities about Java Socket programming, take a closer look at them.( http://blog.csdn.net/shimiso/article/details/8529941 This article does not elaborate on it. Let's take the most commonly used TCP protocol as an example.

Server Socket is used to listen on the specified port, which can be designated at will (since ports below 1024 are usually reserved ports and can not be used at will in some operating systems, so it is recommended to use ports larger than 1024), wait for customer connection requests, after customer connection, session is generated; after the session is completed, the connection is closed.
Client, using Java socket communication to make a connection request to a certain port of a server on the network, once the connection is successful, open the session; after the session is completed, close the Socket. Clients do not need to specify open ports, usually temporarily and dynamically allocate more than 1024 ports.

TCP network connection model:

Android client program generation analysis:

UploadActivity.java  

package com.android.upload;  
import java.io.File;    
import java.io.OutputStream;    
import java.io.PushbackInputStream;    
import java.io.RandomAccessFile;    
import java.net.Socket;    

import android.app.Activity;    
import android.os.Bundle;    
import android.os.Environment;    
import android.os.Handler;    
import android.os.Message;    
import android.view.View;    
import android.view.View.OnClickListener;  
import android.widget.Button;    
import android.widget.EditText;    
import android.widget.ProgressBar;    
import android.widget.TextView;    
import android.widget.Toast;    

import com.android.service.UploadLogService;    
import com.android.socket.utils.StreamTool;  


public class UploadActivity extends Activity {    
    private EditText filenameText;    
    private TextView resulView;    
    private ProgressBar uploadbar;    
    private UploadLogService logService;    
    private boolean start=true;  
    private Handler handler = new Handler(){    
        @Override    
        public void handleMessage(Message msg) {    
            int length = msg.getData().getInt("size");    
            uploadbar.setProgress(length);    
            float num = (float)uploadbar.getProgress()/(float)uploadbar.getMax();    
            int result = (int)(num * 100);    
            resulView.setText(result+ "%");    
            if(uploadbar.getProgress()==uploadbar.getMax()){    
                Toast.makeText(UploadActivity.this, R.string.success, 1).show();    
            }    
        }    
    };    

    @Override    
    public void onCreate(Bundle savedInstanceState) {    
        super.onCreate(savedInstanceState);    
        setContentView(R.layout.main);    

        logService = new UploadLogService(this);    
        filenameText = (EditText)this.findViewById(R.id.filename);    
        uploadbar = (ProgressBar) this.findViewById(R.id.uploadbar);    
        resulView = (TextView)this.findViewById(R.id.result);    
        Button button =(Button)this.findViewById(R.id.button);    
        Button button1 =(Button)this.findViewById(R.id.stop);   
        button1 .setOnClickListener(new OnClickListener() {  

            @Override  
            public void onClick(View v) {  
                start=false;  

            }  
        });  
        button.setOnClickListener(new View.OnClickListener() {    
            @Override    
            public void onClick(View v) {    
                start=true;  
                String filename = filenameText.getText().toString();    
                if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){    
                    File uploadFile = new File(Environment.getExternalStorageDirectory(), filename);    
                    if(uploadFile.exists()){    
                        uploadFile(uploadFile);    
                    }else{    
                        Toast.makeText(UploadActivity.this, R.string.filenotexsit, 1).show();    
                    }    
                }else{    
                    Toast.makeText(UploadActivity.this, R.string.sdcarderror, 1).show();    
                }    
            }    
        });    
    }    
    /**  
     * Upload files  
     * @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){//Add a binding record to the database instead of uploading the file.    
                        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();    
    }    
}    
StreamTool.java  

package com.android.socket.utils;  

import java.io.ByteArrayOutputStream;  
import java.io.File;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.io.InputStream;  
import java.io.PushbackInputStream;  

public class StreamTool {  

     public static void save(File file, byte[] data) throws Exception {  
         FileOutputStream outStream = new FileOutputStream(file);  
         outStream.write(data);  
         outStream.close();  
     }  

     public static String readLine(PushbackInputStream in) throws IOException {  
            char buf[] = new char[128];  
            int room = buf.length;  
            int offset = 0;  
            int c;  
loop:       while (true) {  
                switch (c = in.read()) {  
                    case -1:  
                    case '\n':  
                        break loop;  
                    case '\r':  
                        int c2 = in.read();  
                        if ((c2 != '\n') && (c2 != -1)) in.unread(c2);  
                        break loop;  
                    default:  
                        if (--room < 0) {  
                            char[] lineBuffer = buf;  
                            buf = new char[offset + 128];  
                            room = buf.length - offset - 1;  
                            System.arraycopy(lineBuffer, 0, buf, 0, offset);  

                        }  
                        buf[offset++] = (char) c;  
                        break;  
                }  
            }  
            if ((c == -1) && (offset == 0)) return null;  
            return String.copyValueOf(buf, 0, offset);  
    }  

    /** 
    * Read stream 
    * @param inStream 
    * @return Byte array 
    * @throws Exception 
    */  
    public static byte[] readStream(InputStream inStream) throws Exception{  
            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();  
            byte[] buffer = new byte[1024];  
            int len = -1;  
            while( (len=inStream.read(buffer)) != -1){  
                outSteam.write(buffer, 0, len);  
            }  
            outSteam.close();  
            inStream.close();  
            return outSteam.toByteArray();  
    }  
}  

UploadLogService.java  

package com.android.service;  

import java.io.File;  

import android.content.Context;  
import android.database.Cursor;  
import android.database.sqlite.SQLiteDatabase;  

public class UploadLogService {  
    private DBOpenHelper dbOpenHelper;  

    public UploadLogService(Context context){  
        this.dbOpenHelper = new DBOpenHelper(context);  
    }  

    public void save(String sourceid, File uploadFile){  
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();  
        db.execSQL("insert into uploadlog(uploadfilepath, sourceid) values(?,?)",  
                new Object[]{uploadFile.getAbsolutePath(),sourceid});  
    }  

    public void delete(File uploadFile){  
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();  
        db.execSQL("delete from uploadlog where uploadfilepath=?", new Object[]{uploadFile.getAbsolutePath()});  
    }  

    public String getBindId(File uploadFile){  
        SQLiteDatabase db = dbOpenHelper.getReadableDatabase();  
        Cursor cursor = db.rawQuery("select sourceid from uploadlog where uploadfilepath=?",   
                new String[]{uploadFile.getAbsolutePath()});  
        if(cursor.moveToFirst()){  
            return cursor.getString(0);  
        }  
        return null;  
    }  
}  

DBOpenHelper.java  

package com.android.service;  

import android.content.Context;  
import android.database.sqlite.SQLiteDatabase;  
import android.database.sqlite.SQLiteOpenHelper;  

public class DBOpenHelper extends SQLiteOpenHelper {  

    public DBOpenHelper(Context context) {  
        super(context, "upload.db", null, 1);  
    }  

    @Override  
    public void onCreate(SQLiteDatabase db) {  
        db.execSQL("CREATE TABLE uploadlog (_id integer primary key autoincrement, uploadfilepath varchar(100), sourceid varchar(10))");  
    }  

    @Override  
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
        db.execSQL("DROP TABLE IF EXISTS uploadlog");  
        onCreate(db);         
    }  

}  

main.xml  

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent"  
    >  
<TextView    
    android:layout_width="fill_parent"   
    android:layout_height="wrap_content"   
    android:text="@string/filename"  
    />  

    <EditText    
        android:layout_width="fill_parent"   
        android:layout_height="wrap_content"   
        android:text="022.jpg"  
        android:id="@+id/filename"  
        />  

    <Button    
        android:layout_width="wrap_content"   
        android:layout_height="wrap_content"   
        android:text="@string/button"  
        android:id="@+id/button"  
        />  
    <Button    
        android:layout_width="wrap_content"   
        android:layout_height="wrap_content"   
        android:text="suspend"  
        android:id="@+id/stop"  
        />  
    <ProgressBar   
            android:layout_width="fill_parent"   
            android:layout_height="20px"  
            style="?android:attr/progressBarStyleHorizontal"  
            android:id="@+id/uploadbar"  
            />   
    <TextView    
        android:layout_width="fill_parent"   
        android:layout_height="wrap_content"   
        android:gravity="center"  
        android:id="@+id/result"  
        />     
</LinearLayout>  

AndroidManifest.xml  

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    package="com.android.upload"  
    android:versionCode="1"  
    android:versionName="1.0" >  

    <uses-sdk android:minSdkVersion="8" />  

    <application  
        android:icon="@drawable/ic_launcher"  
        android:label="@string/app_name" >  
        <activity  
            android:name=".UploadActivity"  
            android:label="@string/app_name" >  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  

                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  
    </application>  
    <!-- Access to the network -->  
    <uses-permission android:name="android.permission.INTERNET"/>  
    <!-- stay SDCard Create and delete file permissions in -->  
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>  
    <!-- to SDCard Write data permissions -->  
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  
</manifest>  

Java server:

SocketServer.javapackage com.android.socket.server;  

import java.io.File;  
import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.io.OutputStream;  
import java.io.PushbackInputStream;  
import java.io.RandomAccessFile;  
import java.net.ServerSocket;  
import java.net.Socket;  
import java.text.SimpleDateFormat;  
import java.util.Date;  
import java.util.HashMap;  
import java.util.Map;  
import java.util.Properties;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  

import com.android.socket.utils.StreamTool;  

public class SocketServer {  
    private String uploadPath="D:/uploadFile/";  
    private ExecutorService executorService;// Thread pool  
    private ServerSocket ss = null;  
    private int port;// Monitor port  
    private boolean quit;// Whether to quit  
    private Map<Long, FileLog> datas = new HashMap<Long, FileLog>();// Store breakpoint data, it is better to change to data storage  

    public SocketServer(int port) {  
        this.port = port;  
        // Initialize thread pool  
        executorService = Executors.newFixedThreadPool(Runtime.getRuntime()  
                .availableProcessors() * 50);  
    }  

    // Startup service  
    public void start() throws Exception {  
        ss = new ServerSocket(port);  
        while (!quit) {  
            Socket socket = ss.accept();// Accept client requests  
            // To support multi-user concurrent access, thread pool is used to manage the connection requests of each user.  
            executorService.execute(new SocketTask(socket));// Start a thread to process requests  
        }  
    }  

    // Sign out  
    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());  
                // Get the first line of protocol data from the client: Content-Length = 143253434; filename = xxxx.3gp; sourceid=  
                // If the user uploads the file for the first time, the value of sourceid is empty.  
                String head = StreamTool.readLine(inStream);  
                System.out.println(head);  
                if (head != null) {  
                    // Next, read the various parameter values from the protocol data  
                    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);//Find if the uploaded file has an upload record  
                    }  
                    File file = null;  
                    int position = 0;  
                    if(log==null){//If the uploaded file does not have an upload record, add a trace record to the file  
                        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()){//If the uploaded file is renamed, it is renamed  
                            filename = filename.substring(0, filename.indexOf(".")-1)+ dir.listFiles().length+ filename.substring(filename.indexOf("."));  
                            file = new File(dir, filename);  
                        }  
                        save(id, file);  
                    }else{// If the uploaded file has an upload record, read the last breakpoint location  
                        file = new File(log.getPath());//The path to get the file from the upload record  
                        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"));//Read breakpoint position  
                            }  
                        }  
                    }  

                    OutputStream outStream = socket.getOutputStream();  
                    String response = "sourceid="+ id+ ";position="+ position+ "\r\n";  
                    //When the server receives the request information from the client, it returns the response information to the client: sourceid=1274773833264;position=0.  
                    //Sorceid is generated by the service and uniquely identifies the uploaded file. position indicates where the client starts uploading the file.  
                    outStream.write(response.getBytes());  

                    RandomAccessFile fileOutStream = new RandomAccessFile(file, "rwd");  
                    if(position==0) fileOutStream.setLength(Integer.valueOf(filelength));//Setting File Length  
                    fileOutStream.seek(position);//Start writing data at the location specified by the mobile file  
                    byte[] buffer = new byte[1024];  
                    int len = -1;  
                    int length = position;  
                    while( (len=inStream.read(buffer)) != -1){//Read and write data from input stream to file  
                        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);//The Last Storage Location of Real-time Recording Files  
                        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) {}  
            }  
        }  

    }  

    public FileLog find(Long sourceid) {  
        return datas.get(sourceid);  
    }  

    // Save upload records  
    public void save(Long id, File saveFile) {  
        // In the future, it can be stored through the database.  
        datas.put(id, new FileLog(id, saveFile.getAbsolutePath()));  
    }  

    // When the file is uploaded, delete the record  
    public void delete(long sourceid) {  
        if (datas.containsKey(sourceid))  
            datas.remove(sourceid);  
    }  

    private class FileLog {  
        private Long id;  
        private String path;  

        public FileLog(Long id, String path) {  
            super();  
            this.id = id;  
            this.path = path;  
        }  

        public Long getId() {  
            return id;  
        }  

        public void setId(Long id) {  
            this.id = id;  
        }  

        public String getPath() {  
            return path;  
        }  

        public void setPath(String path) {  
            this.path = path;  
        }  

    }  
}  
ServerWindow.javapackage com.android.socket.server;  

import java.awt.BorderLayout;  
import java.awt.Frame;  
import java.awt.Label;  
import java.awt.event.WindowEvent;  
import java.awt.event.WindowListener;  

public class ServerWindow extends Frame{  
    private SocketServer server;  
    private Label label;  

    public ServerWindow(String title){  
        super(title);  
        server = new SocketServer(7878);  
        label = new Label();  
        add(label, BorderLayout.PAGE_START);  
        label.setText("The server has been started");  
        this.addWindowListener(new WindowListener() {  
            @Override  
            public void windowOpened(WindowEvent e) {  
                new Thread(new Runnable() {           
                    @Override  
                    public void run() {  
                        try {  
                            server.start();  
                        } catch (Exception e) {  
                            e.printStackTrace();  
                        }  
                    }  
                }).start();  
            }  

            @Override  
            public void windowIconified(WindowEvent e) {  
            }  

            @Override  
            public void windowDeiconified(WindowEvent e) {  
            }  

            @Override  
            public void windowDeactivated(WindowEvent e) {  
            }  

            @Override  
            public void windowClosing(WindowEvent e) {  
                 server.quit();  
                 System.exit(0);  
            }  

            @Override  
            public void windowClosed(WindowEvent e) {  
            }  

            @Override  
            public void windowActivated(WindowEvent e) {  
            }  
        });  
    }  
    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        ServerWindow window = new ServerWindow("File upload server");   
        window.setSize(300, 300);   
        window.setVisible(true);  
    }  

}  
StreamTool.javapackage com.android.socket.utils;  

import java.io.ByteArrayOutputStream;  
import java.io.File;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.io.InputStream;  
import java.io.PushbackInputStream;  

public class StreamTool {  

     public static void save(File file, byte[] data) throws Exception {  
         FileOutputStream outStream = new FileOutputStream(file);  
         outStream.write(data);  
         outStream.close();  
     }  

     public static String readLine(PushbackInputStream in) throws IOException {  
            char buf[] = new char[128];  
            int room = buf.length;  
            int offset = 0;  
            int c;  
loop:       while (true) {  
                switch (c = in.read()) {  
                    case -1:  
                    case '\n':  
                        break loop;  
                    case '\r':  
                        int c2 = in.read();  
                        if ((c2 != '\n') && (c2 != -1)) in.unread(c2);  
                        break loop;  
                    default:  
                        if (--room < 0) {  
                            char[] lineBuffer = buf;  
                            buf = new char[offset + 128];  
                            room = buf.length - offset - 1;  
                            System.arraycopy(lineBuffer, 0, buf, 0, offset);  

                        }  
                        buf[offset++] = (char) c;  
                        break;  
                }  
            }  
            if ((c == -1) && (offset == 0)) return null;  
            return String.copyValueOf(buf, 0, offset);  
    }  

    /** 
    * Read stream 
    * @param inStream 
    * @return Byte array 
    * @throws Exception 
    */  
    public static byte[] readStream(InputStream inStream) throws Exception{  
            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();  
            byte[] buffer = new byte[1024];  
            int len = -1;  
            while( (len=inStream.read(buffer)) != -1){  
                outSteam.write(buffer, 0, len);  
            }  
            outSteam.close();  
            inStream.close();  
            return outSteam.toByteArray();  
    }  

}  

The operation results are as follows:
Android front-end control:

Posted by noodle on Tue, 02 Apr 2019 23:15:31 -0700