[Java] 文件上传下载项目(详细注释)
先上代码,最上方注释是文件名称(运行时要用到)
FTServer.java
1 /* 2 FTServer.java 3 */ 4 5 import java.util.*; 6 import java.io.*; 7 8 public class FTServer { 9 10 public static File share = null; 11 12 public static void main(String[] args) throws Exception { 13 14 int port = 4321; 15 16 //加载配置文件 17 Properties p = new Properties(); 18 p.load(FTServer.class.getClassLoader().getResourceAsStream("server.properties")); 19 20 //获取服务器的存储路径 21 share = new File(p.getProperty("share")); 22 23 if(!share.isDirectory()) { 24 System.out.println("share directory not exists or isn't a directory"); 25 System.exit(-4); 26 } 27 28 //读取服务器与客户机交互的端口号 29 port = Integer.parseInt(p.getProperty("port")); 30 31 FTProtocol protocol = new FTProtocol(); 32 AdvancedSupport as = new AdvancedSupport(protocol); 33 NwServer nw = new NwServer(as,port); 34 35 } 36 37 }
FTClient.java
1 /* 2 FTClient.java 3 */ 4 5 import java.net.*; 6 import java.io.*; 7 import java.util.*; 8 9 public class FTClient { 10 11 Socket s = null; 12 DataInputStream dis = null; 13 DataOutputStream dos = null; 14 15 String[] args = null; 16 17 public void start(String server, int port) throws Exception { 18 s = establish(server, port);//new一个socket 19 dis = new DataInputStream(s.getInputStream()); 20 dos = new DataOutputStream(s.getOutputStream()); 21 22 if (args[1].equals("get")) {//获取文件 23 dos.writeInt(3);//执行command为3的操作,对应FTProtocol中的case 3 24 dos.flush();//清空缓冲区数据 25 int files = dis.readInt();//得到目录中文件数量 26 if (files == 0) {//目录中文件为空 27 System.out.println("no files available on the FTServer"); 28 s.close();//关闭套接字 29 System.exit(-1);//退出程序 30 } 31 String[] filenames = new String[files]; 32 for (int i = 0; i < files; i++) { 33 filenames[i] = dis.readUTF();//读取FTProtocol中通过writeUTF(files[i])发来的文件名 34 System.out.println(i + 1 + "\t\t" + filenames[i]);//输出序号和文件名 35 } 36 37 System.out.print("please input your choice:"); 38 39 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 40 String c = br.readLine();//读取输入的命令选项 41 42 if (c.equalsIgnoreCase("q")) {//若输入'Q'或'q'则退出程序 43 s.close();//关闭套接字 44 System.exit(0);//退出程序 45 } 46 47 if (c.equalsIgnoreCase("a")) {//若输入'A'或'a'则下载全部文件 48 for (int i = 0; i < filenames.length; i++) { 49 System.out.println("filenames[i] = " + filenames[i]); 50 download(filenames[i]);//循环下载每一个文件 51 } 52 s.close(); 53 System.exit(0); 54 } 55 int choice = 0; 56 try { 57 choice = Integer.parseInt(c);//将字符串转换成整形 58 } catch (NumberFormatException e) { 59 System.out.println("your input is wrong"); 60 s.close(); 61 System.exit(-2); 62 } 63 64 //输入文件对应序号则下载该文件 65 if (choice >= 1 && choice <= filenames.length) { 66 download(filenames[choice - 1]); 67 } else { 68 System.out.println("your input is wrong"); 69 s.close(); 70 System.exit(-5); 71 } 72 s.close(); 73 System.exit(0); 74 } else if (args[1].equals("put")) {//上传文件 75 File f = new File("C:/_Client/" + args[2]);//args[2]为文件名 76 if (f.isFile()) { 77 upload("C:/_Client/" + args[2]); 78 } else if (f.isDirectory()) {//如果上传的是一个目录 79 String[] filenames = f.list();//将目录中所有文件存入filenames数组 80 if (filenames.length == 0) { 81 s.close(); 82 System.out.println("no files available in the directory"); 83 System.exit(-8); 84 } 85 for (int i = 0; i < filenames.length; i++) {//将目录中的文件列表显示 86 System.out.println(i + 1 + "\t\t" + filenames[i]); 87 } 88 System.out.print("please input your choice:"); 89 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 90 String c = br.readLine(); 91 92 if (c.equalsIgnoreCase("q")) {//若输入'Q'或'q'则退出程序 93 s.close();//关闭套接字 94 System.exit(0);//退出程序 95 } 96 97 if (c.equalsIgnoreCase("a")) {//若输入'A'或'a'则上传全部文件 98 for (int i = 0; i < filenames.length; i++) { 99 String dir = f.getCanonicalPath();//获取文件路径 100 String tf = null; 101 if (dir.endsWith(File.separator)) {//如果以分隔符结尾,则直接加上文件名 102 tf = dir + filenames[i]; 103 } else {//否则加上分隔符再加文件名 104 tf = dir + File.separator + filenames[i]; 105 } 106 //此时tf为文件的绝对路径 107 if(new File(tf).isDirectory()) continue; 108 upload(tf); 109 } 110 s.close(); 111 System.exit(0); 112 } 113 int choice = 0; 114 try { 115 choice = Integer.parseInt(c); 116 } catch (NumberFormatException e) { 117 System.out.println("your input is wrong"); 118 s.close(); 119 System.exit(-2); 120 } 121 //上传第choice个文件 122 if (choice >= 1 && choice <= filenames.length) { 123 String dir = f.getCanonicalPath(); 124 if (dir.endsWith(File.separator)) { 125 upload(dir + filenames[choice - 1]); 126 } else { 127 upload(dir + File.separator + filenames[choice - 1]); 128 } 129 130 } else { 131 System.out.println("your input is wrong"); 132 s.close(); 133 System.exit(-5); 134 } 135 136 } else { 137 s.close(); 138 System.out.println(args[2] + " not exists"); 139 System.exit(-7); 140 } 141 142 s.close(); 143 System.exit(0); 144 145 } 146 147 } 148 149 public Socket establish(String server, int port) { 150 try { 151 Socket s = new Socket(server, port); 152 return s; 153 } catch (Exception e) { 154 e.printStackTrace(); 155 return null; 156 } 157 } 158 159 public void upload(String filename) throws Exception { 160 161 File f = new File(filename); 162 163 if (!f.exists() || !f.isFile()) { 164 System.out.println("it's wrong, maybe it is not a file or not exists"); 165 System.exit(-6); 166 } 167 168 byte[] buffer = new byte[4096]; 169 int rr = 0; 170 171 dos.writeInt(1);//执行FTProtocol中的上传命令 172 dos.writeUTF(f.getName());//将文件名传递给FTProtocol 173 dos.writeLong(f.length());//将文件长度传递给FTProtocol 174 dos.flush();//清空缓存 175 176 FileInputStream fis = new FileInputStream(f); 177 BufferedInputStream bis = new BufferedInputStream(fis); 178 179 while ((rr = bis.read(buffer)) != -1) { 180 dos.write(buffer, 0, rr); 181 dos.flush(); 182 } 183 184 bis.close(); 185 fis.close(); 186 187 } 188 189 public void download(String filename) throws Exception { 190 dos.writeInt(2);//执行FTProtocol中的下载命令 191 dos.writeUTF(filename);//将要下载的文件名传递给FTProtocol 192 dos.flush(); 193 194 //filename = dis.readUTF(); 195 long len = dis.readLong();//获得文件长度 196 197 byte[] buffer = new byte[4096]; 198 long r = 0; 199 int rr = 0; 200 201 //将先下载后的文件输出到C盘的_Client文件夹 202 FileOutputStream fos = new FileOutputStream("C:/_Client/" + filename); 203 BufferedOutputStream bos = new BufferedOutputStream(fos); 204 205 while (r < len) { 206 if (len - r >= buffer.length) {//若文件未传输的部分大于buffer的长度则每次传输buffer.length的字节 207 rr = dis.read(buffer, 0, buffer.length); 208 } else {//将剩余(小于buffer.length)的数据传输 209 rr = dis.read(buffer, 0, (int) (len - r)); 210 } 211 r = r + rr; 212 bos.write(buffer, 0, rr);//将下次传输可接收的字节传递给FTProtocol,rr=-1则传输结束 213 } 214 215 bos.close(); 216 fos.close(); 217 218 System.out.println("download Finished!"); 219 220 } 221 222 public static void main(String[] args) throws Exception { 223 if(args.length==0) { 224 System.out.println("Usage:"); 225 System.out.println("java FTClient host get"); 226 System.out.println("java FTClient host put afile"); 227 System.exit(0); 228 229 } 230 FTClient ftc = new FTClient(); 231 ftc.args = args; 232 ftc.start(args[0], 4321);//args[0]为host 233 } 234 }
FTProtocol.java
1 import java.net.Socket; 2 /* 3 FTProtocol.java 4 */ 5 6 import java.io.*; 7 import java.util.*; 8 9 //与AdvancedSupport都implements IOStrategy,都重写了service()函数 10 public class FTProtocol implements IOStrategy { 11 12 @Override 13 public void service(Socket socket) { 14 String client = socket.getInetAddress().getHostName() + "(" + socket.getInetAddress().getHostAddress() + ")"; 15 16 try { 17 InputStream is = socket.getInputStream(); 18 OutputStream os = socket.getOutputStream(); 19 DataInputStream dis = new DataInputStream(is); 20 DataOutputStream dos = new DataOutputStream(os); 21 22 String filename = null; 23 long len = 0; 24 byte[] buffer = new byte[4096]; 25 long r = 0; 26 int rr = 0; 27 28 while (true) { 29 int command = dis.readInt(); // 读取FTClient中通过writeInt()传来的命令 30 switch (command) { 31 case 1: // file upload 32 filename = dis.readUTF();//接收文件名 33 len = dis.readLong();//接收文件长度 34 35 //将用户上传文件存到FTServer.share目录中 36 FileOutputStream fos = new FileOutputStream(new File(FTServer.share, filename)); 37 BufferedOutputStream bos = new BufferedOutputStream(fos); 38 r = 0; 39 rr = 0; 40 41 while (r < len) { 42 if (len - r >= buffer.length) { 43 rr = dis.read(buffer, 0, buffer.length); 44 } else { 45 rr = dis.read(buffer, 0, (int) (len - r)); 46 } 47 48 r = r + rr; 49 bos.write(buffer, 0, rr); 50 } 51 52 bos.close(); 53 fos.close(); 54 break; 55 case 2: // file download 56 filename = dis.readUTF(); 57 //dos.writeUTF(filename); 58 File t = new File(FTServer.share, filename); 59 //System.out.println("FTServer.share = " + FTServer.share); 60 dos.writeLong(t.length());//将文件长度传递给FTClient 61 dos.flush();//清空缓存 62 FileInputStream fis = new FileInputStream(t); 63 BufferedInputStream bis = new BufferedInputStream(fis); 64 65 while ((rr = bis.read(buffer)) != -1) {//接收到下次可发送的字节数,-1结束 66 dos.write(buffer, 0, rr); 67 dos.flush(); 68 } 69 70 bis.close(); 71 fis.close(); 72 break; 73 74 case 3: // list files 75 String[] files = FTServer.share.list();//将share目录下所有文件名存入files数组 76 List<String> list = new LinkedList<String>(); 77 for(int i=0;i<files.length;i++) { 78 if(new File(FTServer.share, files[i]).isDirectory()) 79 continue;//如果是一个目录则不显示 80 list.add(files[i]); 81 } 82 83 files = list.toArray(new String[0]);//将List<String>转换成String[] 84 85 dos.writeInt(files.length);//将文件数量传给FTClient 86 dos.flush();//清空缓存 87 for (int i = 0; i < files.length; i++) { 88 dos.writeUTF(files[i]);//将每一个文件名传给FTClient 89 } 90 dos.flush();//清空缓存 91 break; 92 } 93 } 94 } catch (Exception e) { 95 if (e instanceof EOFException) { 96 System.out.println(client + " disconnected"); 97 } else { 98 e.printStackTrace(); 99 } 100 101 } 102 } 103 }
AdvancedSupport.java
1 /* 2 AdvancedSupport.java 3 */ 4 5 import java.net.*; 6 import java.io.*; 7 import java.util.*; 8 9 //与FTProtocol都implements IOStrategy,都重写了service()函数 10 public class AdvancedSupport implements IOStrategy { 11 private ArrayList threads = new ArrayList();//开线程数组 12 private final int INIT_THREADS = 10;//初始线程数 13 private final int MAX_THREADS = 100;//最大线程数 14 private IOStrategy ios = null;//初始化 15 16 //构造函数 17 public AdvancedSupport(IOStrategy ios) { 18 this.ios = ios; 19 20 for (int i = 0; i < INIT_THREADS; i++) { 21 IOThread t = new IOThread(ios); 22 t.start(); 23 threads.add(t); 24 } 25 try { 26 Thread.sleep(300); 27 } catch (Exception e) { 28 } 29 } 30 31 public void service(Socket socket) { 32 IOThread t = null; 33 boolean found = false; 34 35 //顺序找到线程中第一个等待的IOThread,将found置为true并退出循环 36 for (int i = 0; i < threads.size(); i++) { 37 t = (IOThread) threads.get(i); 38 if (t.isIdle()) { 39 found = true; 40 break; 41 } 42 } 43 44 //若Thread中不存在等待的线程,则新建一个线程并启动 45 if (!found) { 46 t = new IOThread(ios); 47 t.start(); 48 try { 49 Thread.sleep(30); 50 } catch (Exception e) { 51 } 52 //将新建线程添加到threads数组中 53 threads.add(t); 54 } 55 56 //将socket赋值为传来的socket参数 57 t.setSocket(socket); 58 } 59 } 60 61 class IOThread extends Thread { 62 private Socket socket = null; 63 private IOStrategy ios = null; 64 65 public IOThread(IOStrategy ios) { 66 this.ios = ios; 67 } 68 69 public boolean isIdle() { 70 return socket == null; 71 } 72 73 public synchronized void setSocket(Socket socket) { 74 this.socket = socket; 75 notify();//唤醒 76 } 77 78 public synchronized void run() { 79 while (true) { 80 try { 81 wait();//等待到setSocket执行notify()后,调用service() 82 ios.service(socket); 83 socket = null;//service()结束后,套接字置为空 84 } catch (Exception e) { 85 e.printStackTrace(); 86 } 87 } 88 } 89 };
IOStrategy.java
1 /* 2 IOStrategy.java 3 */ 4 5 import java.net.*; 6 7 /* 8 提供协议策略定义 9 */ 10 11 public interface IOStrategy 12 { 13 public void service(Socket socket); 14 }
NwServer.java
1 /* 2 NwServer.java 3 */ 4 5 import java.net.*; 6 7 /* 8 实现网络通信,可以服务于任何应用,没有提供协议, 9 也就是说NwServer可以适用于任何协议。 10 */ 11 12 public class NwServer 13 { 14 private int port = 4321; //没有处理端口 15 16 public NwServer(IOStrategy io, int port) throws Exception { 17 //它只负责接受客户端的连接请求,建立网络建立(socket连接) 18 //然后将连接提交给协议处理程序。 19 20 this.port = port; 21 22 ServerSocket server = new ServerSocket(port); 23 System.out.println("FTServer is ready"); 24 25 while(true) 26 { 27 //接受客户端的连接请求 28 Socket socket = server.accept(); 29 30 //获取客户端地址 31 InetAddress ia = socket.getInetAddress(); 32 System.out.println(ia.getHostName() + "(" + ia.getHostAddress() + ") connected."); 33 34 //将连接提交给协议处理程序 35 io.service(socket); 36 } 37 } 38 }
server.properties配置文件:
1 share=C:\\_Server 2 port=4321
效果展示:
首先看一下客户端和服务器端的存储空间内的文件:
服务器:
客户端:
1.编译
2.再开一个窗口,分别运行FTServer和FTClient
(1)get命令,得到服务器中的全部文件列表显示(文件夹不显示)
(2)·choice输入文件序号则下载对应文件
·choice输入a或A则下载全部文件
·choice输入q或Q则退出
(3)put命令,客户端向服务器上传文件(若是一个文件夹则显示文件夹中内容)
·直接上传文件名
·上传文件夹
choice选项基本和下载时一样:
输入a或A则上传全部文件,输入q或Q则退出,输入文件序号则上传对应文件,不再截图。