目标:服务器提供一个目录下载,服务端可以任意选择其中的文件下载
具体思路代码中有注释
/**
* 服务器
*
*/
public class Server1 {
public static void main(String[] args) throws IOException {
File dir = new File("D:/test");
// 创建一个服务
@SuppressWarnings("resource")
ServerSocket ss = new ServerSocket(8888);
System.out.println("服务器已启动");
while(true){
Socket s = ss.accept();
String addr = s.getInetAddress().getHostAddress();
System.out.println("客户端【"+addr+"】已连接");
System.out.println("客户端【"+addr+"】开始下载...");
new FileService(s, dir).start();
System.out.println("客户端【"+addr+"】下载完成");
}
}
}
/**
* 提供文件服务的类
*
*/
public class FileService extends Thread{
private Socket s;
private File file;
public FileService(Socket s, File file){
this.s = s;
this.file = file;
}
@Override
public void run() {
BufferedInputStream bis = null;
try {
// 1. 获取文件列表(仅标准文件) .... 之前有考虑效率问题将获取文件信息放到Server1中,可是这样就固定死了目录.放到这里的话在目录内容修改后进来的Socket也可以获取到
File[] files = file.listFiles(new FileFilter(){
@Override
public boolean accept(File pathname) {
return pathname.isFile();
}
});
// 准备一个Map集合以编号->文件的方式存储文件信息
Map<String, File> map = new HashMap<>();
for (int i = 0; i < files.length; i++) {
map.put(String.valueOf(i + 1), files[i]);
}
// 2. 将文件列表发送到客户端
ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
oos.writeObject(map);
oos.flush();
// 3. 接收客户端的指令(需要下载的文件编号)
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String no = br.readLine();
// 4. 根据文件编号找到文件并获取该文件的输入流
File file = map.get(no);
bis = new BufferedInputStream(new FileInputStream(file));
// 5. 根据Socket的输出流将读取的文件内容写出去
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
CopyUtils.copy(bis, bos, 1024);
bos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally{
try {
if(s != null){
s.close();
}
if(bis != null){
bis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 客户端
*/
public class Client1 {
// 获取目录中文件
public static Map<String,File> getFile(Socket s) throws IOException, ClassNotFoundException {
// 获取基于Socket的对象输入流, 并读取传输过来的文件map
ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
Map<String, File> map = (Map<String, File>) ois.readObject();
Set<String> keys = map.keySet();
Iterator<String> it = keys.iterator();
while (it.hasNext()) {
String key = it.next();
System.out.println("【" + key + "】" + map.get(key).getName());
}
return map;
}
// 将选择的文件编号发送给服务端,然后返回对应文件的名称
public static String sendNum(Socket s, Map<String,File> map) throws IOException{
System.out.println("请输入需要下载的文件编号");
PrintWriter pw = new PrintWriter(s.getOutputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String no = br.readLine();
pw.println(no);
pw.flush();
return map.get(no).getName();
}
public static void main(String[] args) throws UnknownHostException, IOException, ClassNotFoundException {
// 指定下载目录
File dir = new File("D:/test/test1");
// 1. 连接到指定地址、端口的服务
Socket s = new Socket("192.168.4.126", 8888);
// 获取基于Socket的对象输入流, 并读取传输过来的文件map
Map<String,File> map = getFile(s);
// 2. 获取控制台输入流和基于Socket的对象输出流,给服务端发送选项
String fname = sendNum(s, map);
// 3. 准备本地文件的输出流,存储来自Socket的输入流中文件数据
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(dir, fname)));
System.out.println("开始下载");
CopyUtils.copy(bis, bos, 1024);
System.out.println("下载完成");
s.close();
bos.close();
}
}
/**
* 服务端和客户端中都得用到文件复制,故将其封装工具类中
*/
public class CopyUtils {
public static void copy(InputStream is, OutputStream os, int length) throws IOException{
int len = 0;
byte[] b = new byte[length];
while((len = is.read(b)) != -1){
os.write(b,0,len);
}
}
}