这几天在自学Socket网络编程时突然要用到文件和文本同时传输到服务器,但是在网上找了半天页找不到具体的结局办法,最后在不断琢磨之下终于解决了这个问题,在传输数据时使用的是Java中的ObjectInputStream 和 ObjectOutputStream
对象流,这个流可以封装复杂的数据在网络中进行传输,发送涉及到的类需要实现Serializable
接口,是一个标志接口,用于序列化的,没有任何的方法需要实现。废话不多说,直接上代码
第一种方案:
Student类,用于封装数据进行传输和解析
public class Student implements Serializable {
private String username;
private String sex;
private byte[] file;
private String filename;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public byte[] getFile() {
return file;
}
public void setFile(byte[] file) {
this.file = file;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
@Override
public String toString() {
return "Student{" +
"username='" + username + '\'' +
", sex='" + sex + '\'' +
", filename='" + filename + '\'' +
'}';
}
}
客户端:
public class ClientSocket {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",9999);
// 获得Socket输出字节流
OutputStream os = socket.getOutputStream();
Student student = new Student();
student.setUsername("765652244");
student.setSex("男");
// 获取文件地址
File file = new File("C:\\Users\\网络杀手\\Desktop\\image\\soft\\bg.png");
// 将文件名保存到Student中
student.setFilename(file.getName());
// 获得文件字节输入流
FileInputStream fis = new FileInputStream(file);
// 将文件字节流保存至字节输出缓冲流
ByteArrayOutputStream bos = new ByteArrayOutputStream(fis.available());
byte[] b = new byte[1024];
int len = -1;
while((len = fis.read(b)) != -1) {
bos.write(b, 0, len);
}
// 将得到的文件字节转换为字节数组
student.setFile(bos.toByteArray());
// 将socket输出流进行转换
ObjectOutputStream oos =new ObjectOutputStream(os);
// 将对象输出流发送到服务器
oos.writeObject(student);
fis.close();
bos.close();
oos.flush();
oos.close();
System.out.println("发送成功");
}
}
服务器:
public class ServerSocketDome {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ServerSocket serverSocket = new ServerSocket(9999);
// 监听9999端口,等待客户端连接,如果没有连接,将会一直阻塞
Socket accept = serverSocket.accept();
// 获得数据输入流,
InputStream is = accept.getInputStream();
// 对数据输入流进行转换
ObjectInputStream ois = new ObjectInputStream(is);
// 将获得的数据转换为Studen类
Student o = (Student)ois.readObject();
System.out.println("用户名:"+o.getUsername()+"\n"+"性别:"+o.getSex());
System.out.println("文件名:"+o.getFilename());
// 将获得的文件保存至磁盘中
File file = new File("D:\\"+o.getFilename());
// 获得输出流,准备将内存中的数据写到磁盘
FileOutputStream fos = new FileOutputStream(file);
// 将Student类中或的文件字节写入磁盘
fos.write(o.getFile());
fos.close();
serverSocket.close();
accept.close();
is.close();
ois.close();
System.out.println("保存成功,文件名是:"+o.getFilename()+"\n存在在:"+file+"目录下");
}
}
结果
第二中方案:
不需要封装类,也不需要实现Serializable
接口
服务端:
public class ServerSocketDome1 {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept();
// 对象流
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
// 读取文件名
String str = ois.readUTF();
System.out.println("文件名:"+str);
// 读取文件大小
long l = ois.readLong();
System.out.println("文件大小为:"+l);
// 这是使用的是缓冲流,避免了文件大时内存溢出
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("D:\\"+str)));
byte[] buffer = new byte[1024*1024];
int len = -1 ;
while ((len=ois.read(buffer))!=-1){
bos.write(buffer,0,len);
}
ois.close();
bos.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端:
public class Client1 {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1",9999);
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
File file = new File("C:\\Users\\网络杀手\\Desktop\\image\\soft\\bg.png");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
// 写入文件名
oos.writeUTF(file.getName());
// 获得文件大小
oos.writeLong(file.length());
byte[] buffer = new byte[1024*1024];
int len =-1;
while ((len=bis.read(buffer))!=-1){
oos.write(buffer,0,len);
}
oos.flush();
os.close();
oos.close();
fis.close();
bis.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
结果
两种方案中明显是方案二要简便些,但是各有个的优点,方案一中发送的数据需要封装,解析时候方便(可以将ByteArrayOutputStream换成BufferedInputStream
)如果是多个文件,对Student类中的属性进行一下变更,就能够实现,而第二种方案中需要使用Gson数据封装之后发送和解析,对于负杂的数据时还是要进行封装之后才能发送,否则服务器不好解析。