This article introduces how to use existing available libraries in Java to write FTP client code and develop it into Applet controls to make batch, large files upload and download controls based on the web. Based on a series of FTP customer libraries, the article provides detailed explanations and code implementations on one of the more common and powerful j-ftp library, such as progress bars, breakpoint continuation, mapping of internal and external networks, and callback JavaScript functions in Applet. It is hoped that this article will play a role in attracting the jade.
1. Introduction
During the implementation of a project, a web-based file upload and download requirement has emerged. Users across the province (or nationwide) need to upload some files to the file server in a certain center. These documents are used for some large-scale engineering construction, which may involve construction projects worth tens of millions or even hundreds of millions of yuan. Files have three distinct characteristics: one is that the file is large, which may reach 50M; the second is that the number of files is large, which may be about 15; the third is that digital signatures and data encryption are required in terms of data security.
First of all, consider the HTTP-based transmission method. However, through comparison, I quickly found that the above needs were met:
1: Uploading using HTTP protocol seems to be more suitable for web programming convenience; uploading files less than 1M is slightly faster than uploading files using FTP protocol. But there may be nothing to do about the transfer of batch and large files. Of course, it also has its advantages, such as unlike FTP, it is necessary to start an FTP service on the server side.
2: Uploading files with files larger than 1M using the FTP protocol is faster than HTTP. The larger the file, the upload speed will be several times faster than the HTTP upload speed. And write programs in Java; FTP is more convenient than HTTP.
The author once used VB and wrote ActiveX controls to upload and download batch files, and its functions are also very powerful. It is just that since there is no special digital signature for CAB files or OCX, it is necessary to perform tedious settings for the client, such as setting up a secure site, reducing the security level of the client, etc., some solutions have been abandoned.
At the same time, considering that the files need to be digitally signed and encrypted on the client, it was decided to use Applet to implement them. . Before uploading the file, the local USBKEY key information can be obtained from the client to complete the encryption and signature processing of the uploaded file. Although using Applet requires the installation of JRE runtime environment on the client, which brings some inconvenience to the management and use of the client, it may be a relatively small price to compare the security of such a large number of files and files.
To summarize the operating environment:
FTP server side: Serv-U, a professional FTP server side program, there are ready-made software downloads on the Internet, of course readers may also write a server-side FTP file receiving program to explain. If there are no special requirements or functions, Serv-U should be able to meet our general upload and download needs;
Client: Java applet, a technology that made Java popular back then claimed to be comparable to Microsoft's ActiveX. Of course, now Java has Java FX. Is it a substitute for Applet?
Application environment: Internet, ultimate purpose.
2. Choice of Java FTP client library
Let's imagine a scenario - we want to write a pure Java application that uploads and downloads files from an FTP server running on a remote computer; we also want to get basic file information for those remote files for download, such as file name, data or file size.
While it is possible and perhaps interesting to write an FTP protocol handler from scratch, the work is also difficult, long and potentially dangerous. Because we are unwilling to spend time, energy, or money on writing such a processing program ourselves, we turn to reusable components that already exist. And a lot of libraries are available online.
Finding an excellent Java FTP client library that suits our needs is not as simple as it seems. On the contrary, this is a very painful and complicated task. First, it takes some time to find an FTP client library. Second, after we find all existing libraries, which one should we choose? Each library is suitable for different needs. These libraries are not equivalent in performance and have fundamental differences in their design. Each library has its own characteristics and uses different terms to describe them. Therefore, evaluating and comparing FTP client libraries is a difficult task.
Using reusable components is an advocated approach, but in this case it is often discouraging at the beginning. Later, I might be a little ashamed: after choosing a good FTP library, the subsequent work is very simple, just follow the simple rules. At present, there are many publicly and free ftp client library, such as simpleftp, J-ftp, etc., and many other ftpclients. As shown in the following table, the table cannot be listed all. If readers have a better client FTP class library, please make further supplements.
In this article, the author adopts J-ftp. This is an open source and very powerful client FTP library. The author likes it very much, and also recommends it to all readers. Forget it for free and make an advertisement for it.
3. Basic functions
1. Log in
FTP is used for file transfer, but in essence, Java.net.socket is used for communication. The following code is just one of the login methods of the class net.sf.jftp.net.FtpConnection. Of course, in order to save layout and explain some principles clearly in the following code, the author has removed some unnecessary codes, such as logs and other codes. For the complete code, please refer to the source code of J-ftp or the author's example source code. The following code examples are the same:
public int login(String username, String password) { this.username = username; this.password = password; int status = LOGIN_OK; jcon = new JConnection(host, port); if(jcon.isThere()) { in = jcon.getReader(); if(getLine(POSITIVE) == null)//FTP220_SERVICE_READY) == null) { ok = false; status = OFFLINE; } if(!getLine(loginAck).startsWith(POSITIVE))//FTP230_LOGGED_IN)) { if(success(POSITIVE))//FTP230_LOGGED_IN)) { } else { ok = false; status = WRONG_LOGIN_DATA; } } } else { if(msg) { Log.debug("FTP not available!"); ok = false; status = GENERIC_FAILED; } } if(ok) { connected = true; system(); binary(); String[] advSettings = new String[6]; if(getOsType().indexOf("OS/2") >= 0) { LIST_DEFAULT = "LIST"; } if(LIST.equals("default")) { //just get the first item (somehow it knows first is the //FTP list command) advSettings = LoadSet.loadSet(Settings.adv_settings); //*** IF FILE NOT FOUND, CREATE IT AND SET IT TO LIST_DEFAULT if(advSettings == null) { LIST = LIST_DEFAULT; SaveSet s = new SaveSet(Settings.adv_settings, LIST); } else { LIST = advSettings[0]; if(LIST == null) { LIST = LIST_DEFAULT; } } } if(getOsType().indexOf("MVS") >= 0) { LIST = "LIST"; } //*** fireDirectoryUpdate(this); fireConnectionInitialized(this); } else { fireConnectionFailed(this, new Integer(status).toString()); } return status; } In this login method, there is a JConnection class, which is responsible for establishing socket sockets. At the same time, this class is a separate thread. The advantage is that in order to cooperate with interface changes, network socket connections and other tasks are processed as separate threads, which is conducive to the friendliness of the interface. Below is the run method of net.sf.jftp.net.JConnection class. Of course, the startup of this thread is started in the constructor of JConnection class.
public void run() { try { s = new Socket(host, port); localPort = s.getLocalPort(); //if(time > 0) s.setSoTimeout(time); out = new PrintStream(new BufferedOutputStream(s.getOutputStream(), Settings.bufferSize)); in = new BufferedReader(new InputStreamReader(s.getInputStream()), Settings.bufferSize); isOk = true; // } } catch(Exception ex) { ex.printStackTrace(); Log.out("WARNING: connection closed due to exception (" + host + ":" + port + ")"); isOk = false; try { if((s != null) && !s.isClosed()) { s.close(); } if(out != null) { out.close(); } if(in != null) { in.close(); } } catch(Exception ex2) { ex2.printStackTrace(); Log.out("WARNING: got more errors trying to close socket and streams"); } } established = true; }The socket in this run method will explain here that this type of implementation of client sockets (also called "sockets"), which are communication endpoints between two machines. The actual work of the socket is performed by an instance of the SocketImpl class. An application implements a socket factory that creates sockets by changing the socket factory that can configure itself to create sockets that are suitable for the local firewall. For specific instructions, please refer to the API instructions of JDK5, preferably in Chinese. hehe.
2 Upload and download
Uploading files can be divided into multi-threaded and single-threaded, which is relatively simple in single-threaded situations, while in multi-threaded situations, more things to be handled and more careful. Below is the upload handleUpload method of net.sf.jftp.net.FtpConnection. Two different types, single thread and multi-thread, have been considered.
public int handleUpload(String file, String realName) { if(Settings.getEnableMultiThreading() && (!Settings.getNoUploadMultiThreading())) { Log.out("spawning new thread for this upload."); FtpTransfer t; if(realName != null) { t = new FtpTransfer(host, port, getLocalPath(), getCachedPWD(), file, username, password, Transfer.UPLOAD, handler, listeners, realName, crlf); } else { t = new FtpTransfer(host, port, getLocalPath(), getCachedPWD(), file, username, password, Transfer.UPLOAD, handler, listeners, crlf); } lastTransfer = t; return NEW_TRANSFER_SPAWNED; } else { if(Settings.getNoUploadMultiThreading()) { Log.out("upload multithreading is disabled."); } else { Log.out("multithreading is completely disabled."); } return (realName == null) ? upload(file) : upload(file, realName); }} In the case of multi-threading, there is a separate class net.sf.jftp.net.FtpTransfer. Of course, in the case of multi-threading, this class must be a separate thread. Similar to JConnection, the startup of its thread is also started in the constructor. In its run method, the file is read and transmitted.
public void run() { if(handler.getConnections().get(file) == null) { handler.addConnection(file, this); } else if(!pause) { Log.debug("Transfer already in progress: " + file); work = false; stat = 2; return; } boolean hasPaused = false; while(pause) { try { runner.sleep(100); if(listeners != null) { for(int i = 0; i < listeners.size(); i++) { ((ConnectionListener) listeners.elementAt(i)).updateProgress(file, PAUSED, -1); } } if(!work) { if(listeners != null) { for(int i = 0; i < listeners.size(); i++) { ((ConnectionListener) listeners.elementAt(i)).updateProgress(file, REMOVED, -1); } } } } catch(Exception ex) { } hasPaused = true; } while((handler.getConnectionSize() >= Settings.getMaxConnections()) && (handler.getConnectionSize() > 0) && work) { try { stat = 4; runner.sleep(400); if(!hasPaused && (listeners != null)) { for(int i = 0; i < listeners.size(); i++) { ((ConnectionListener) listeners.elementAt(i)).updateProgress(file, QUEUED, -1); } } else { break; } } catch(Exception ex) { ex.printStackTrace(); } } if(!work) { if(listeners != null) { for(int i = 0; i < listeners.size(); i++) { ((ConnectionListener) listeners.elementAt(i)).updateProgress(file, REMOVED, -1); } } handler.removeConnection(file); stat = 3; return; } started = true; try { runner.sleep(Settings.ftpTransferThreadPause); } catch(Exception ex) { } con = new FtpConnection(host, port, remotePath, crlf); con.setConnectionHandler(handler); con.setConnectionListeners(listeners); int status = con.login(user, pass); if(status == FtpConnection.LOGIN_OK) { File f = new File(localPath); con.setLocalPath(f.getAbsolutePath()); if(type.equals(UPLOAD)) { if(newName != null) { transferStatus = con.upload(file, newName); } else { transferStatus = con.upload(file); } } else { transferStatus = con.download(file, this.newName); } } if(!pause) { handler.removeConnection(file); } } As for the download process, because it is an inverse process of uploading, it is similar to the upload method and writing method. For some reasons, the code is not listed, but its ideas and ideas are exactly the same. Please refer to the source code for readers.
4. Progress bar
It can be imagined that if there is no prompt during the upload or download process, the user cannot judge whether the task is completed or whether the task is dead, and the user is often misleading due to the upload time or the download time being too long. Therefore, the progress bar is very important and practical.
The implementation of the progress bar is actually very simple. It is to open two threads in the program. The first thread is used to dynamically change the value of the progress bar on the interface, while the second thread forms a loop during the upload or download process. In this loop, a certain number of data such as 8192 bytes are read each time. Then after passing this data, call the updateProgress method in the first thread to update the value of the interface progress bar.
During the upload or download process (see the run method of the FtpTransfer class in the previous section), you can view the con.upload(file, newName) method, the code is as follows.
public int upload(String file, String realName, InputStream in) { hasUploaded = true; Log.out("ftp upload started: " + this); int stat; if((in == null) && new File(file).isDirectory()) { shortProgress = true; fileCount = 0; baseFile = file; dataType = DataConnection.PUTDIR; isDirUpload = true; stat = uploadDir(file); shortProgress = false; //System.out.println(fileCount + ":" + baseFile); fireProgressUpdate(baseFile, DataConnection.DFINISHED + ":" + fileCount, -1); fireActionFinished(this); fireDirectoryUpdate(this); } else { dataType = DataConnection.PUT; stat = rawUpload(file, realName, in); try { Thread.sleep(100); } catch(Exception ex) { } fireActionFinished(this); fireDirectoryUpdate(this); } try { Thread.sleep(500); } catch(Exception ex) { } return stat; }This method is responsible for uploading a certain number of bytes. In fact, it is called the rawUpload method. It is not listed here. Please refer to the source code. After passing this byte data, the updateProgressBar() method in the main thread is called by calling the fireActionFinished() method. Actually the code is as follows:
protected void updateProgressBar() { int percent = (int) (((float) lFileCompleteSize / (float) lFileSize) * 10000F); pbFile.setValue(percent); // System.out.println("================================================="+percent); pbFile.setString(lFileCompleteSize / 1024L + "/" + lFileSize / 1024L + " kB"); percent = (int) (((float) lTotalCompleteSize / (float) lTotalSize) * 10000F); pbTotal.setString(lTotalCompleteSize / 1024L + "/" + lTotalSize / 1024L + "kB"); pbTotal.setValue(percent); repaint(); } Two progress bars are used above. The first progress bar represents the upload or download progress of the current file, and the second progress bar represents the progress of all files download or upload. At the same time, in order to generate a more obvious progression or change of progress, the maximum value of the progress bar is set to 10,000 through pbFile.setMaximum(10000) and pbTotal.setMaximum(10000), rather than the 100 we usually set. The author believes that this is better, because sometimes when uploading or downloading, the changes may be relatively small due to network reasons. If set to 100, the change is not particularly obvious.
The above is the basic article for uploading and downloading large batches of FTP files. I hope it will be helpful to everyone's learning, and I hope everyone will support Wulin.com more.