面试题:如果使用Java开发多线程传输文件,你如何实现。(只需要说明技术和思路)
BS结构
H5,文件组件标签,有多个属性,文件切割相关属性。把一个文件切割,从文件的第几个字节开始读取,读取多少字节。
服务端本身就是一个多线程,
Commons-Fileupload
一个进程有多个线程,一个线程有多个协程
CS结构
使用多线程技术和RandomAccessFile技术实现。
多线程,提高效率使用ExecutorService线程池,优化代码。
RandomAccessFile是随机文件读写流,打开文件,做随机读写。通过分隔文件,跳过不需要读写的字节数,进行读写操作。
socket+IO
java.io.RandomAccessFile 文件随机读写流
上传:
client:
class ClientMain{
public static void main(String[] args){
Socket s = new Socket(ip, port);
File file = new File(path);
FileInputStream fis = new FileInputStream(file);
// 文件的总体长度
long length = fis.available();
// 设置10线程上传。
long point = length/10;
// 让服务器创建文件
OutputStream output = s.getOutputStream();
output.write("{\"filename\"=\"xxx\"}".byteArray());
// 阻塞等待,等待多线程上传结束。
// 创建一个线程池,固定容量,只有10个线程。
ExecutorService es = ExecutorServices.newFixedThreadPool(10);
ArrayList fArray = new ArrayList();
for(int i = 0; i < 10; i++){
Socket upload = new Socket(ip, port);
int index = (i * point);
int endIndex = 0;
if(i != 9){
endIndex = (i + 1) * point;
}else{
endIndex = length;
}
Future<Object> f =
es.submit(new UpdateClient(file, index, endIndex, upload));
fArray.add(f);
}
int flag = 10;
while(flag > 0){
for(Future f : fArray){
try{
f.get(10, TimeUnit.MILLSSECOND);
flag--;
fArray.remove(f);
}catch(Exception e){}
}
}
// 所有的线程都执行结束了。文件上传结束。
s.close();
}
}
class UploadClient implements Callable{
private File file; // 要上传的文件
private int index; // 要读取的文件的起始下标
private int endIndex; // 要读取的文件的结束下标
private Socket socket; // 客户端对象
public UploadClient(File file, int index, int endIndex, Socket socket){
// 赋值
}
public Object call(){
RandomAccessFile raf = new RandomAccessFile(file);
raf.skipBytes(index);
byte[] temp = new byte[endIndex - index];
raf.read(temp, index, endIndex - index);
OutputStream output = socket.getOutputStream();
// 通知服务器,本次连接上传的是那一个文件,是这个文件的那一个部分。
output.write("{\"filename\"=\"\",\"index\"=?,\"endIndex\"=?}".getBytes());
// 通知服务器,描述信息结束,后续的都是文件内容。
output.write("\n".getBytes());
output.flush();
output.write(temp);
output.flush();
raf.close();
socket.close();
return "ok";
}
}
server:
class ServerMain{
public static void main(String [] args){
ServerSocket ss = new ServerSocket(port);
while(true){
Socket s = ss.accept();
new Thread(new ServerHandler(s)).start();
}
}
}
class ServerHandler implements Runnable{
private Socket s;
public ServerHandler(Socket s){}
public void run(){
InputStream input = s.getInputStream();
BufferedReader reader = new BufferedReader(input);
String line = reader.readLine();
// 处理字符串line,如果是{filename:xxx}, 创建文件
ObjectMapper om = new ObjectMapper();
Map map = om.readObject(line, Map.class);
if(map.size() == 1){// 创建文件}
else{// 多线程写入操作
// 读取文件内容,做写入操作
String filename = map.get("filename");
long index = map.get("index");
long endIndex = map.get("endIndex");
RandomAccessFile raf = new RandomAccessFile(filename, "append");
raf.skipBytes(index);
input.read();
raf.write();
}
}
}
迅雷