socket编程机制:客户端与服务端通过socket套接字连接后都会返回一个实例对象,分别保存这个对象,就相当于保存的对方的地址。Socket理解为一个可以连通网络上不同计算机程序之间的管道,把一堆数据从管道的A端扔进去,则会从管道的B端出来。管道的端口由两个因素来唯一确认,即机器的IP地址和程序所使用的端口号。端口号就是程序员指定的一个数字,许多著名的木马程序在网络上扫描不同的端口号就是为了获取一个可以连通的端口从而进行破坏。程序不要使用太小的端口号,它们一般被系统占用了,也不要使用一些常用的端口,一般来说使用1000~5000之内的端口比较好。
实现功能:
1.实现手机相册中的单个图片上传到电脑指定路径。(手机相册存在固定位置/sdcard/DCIM/Camera/...)
输入*.jpg,*.jpeg等
2.修改之前上传固定位置的单个图片为输入相册中的多个图片名称,用逗号“,”分隔开,然后上传到电脑,提示用户是否上传成功。
3.因为上传照片很难分清哪个照片需要添加照片描述,后来想使用ExpandableListView下拉列表让用户选择图片内容类型,选择后自动添加到输入框和图片名称以“:”分开,上传电脑以此描述类型命名为图片名称,为防止图片重名,前面加上上传时原图片名称,上传同一张图片时才会发生覆盖,不会引起图片丢失,还能直观查看图片内容描述。
4.改善项目功能范围太小的缺陷,添加输入手机内某个文件所在路径,就可获取路径下的所有图片全部上传到电脑。
注意:测试时最好用真机,否则虚拟机虚拟内存没有图片无法测试功能是否成功。
1.首先搭建好客户端环境,新建android-project ,新建activity
将手机中的图片发送字节给服务端
2.创建手机界面布局mainactivity.xml
界面中有textview、EditText、Button、ExpandableListView控件
这里的界面效果:
3.MainActivity----socket客户端:接收手机图片信息并处理
完成任务1.在固定路径下(sdcard/./.)查找图片文件,输入图片名称实现上传,
2.实现输入手机内图片文件的绝对路径,然后点击上传按钮,可将图片上传到电脑指定位置。服务器来接收图片并指定存放位置。
3.实现选择图片的类型并将其图片复制后重命名,以便用户在电脑分辨图片内容并使用。
4.ExpandableListView下拉列表的的学习和使用,重写下拉列表适配器,显示自定义样式
5.截取原图片的名称使其与图片内容类型共同组成上传后的图片名称。例如:a.jpg 图片内容类型为旅游,则重命名的名称为旅游a.jpg
public class UploadPhotoActivity extends Activity { private Socket socket; private String ip = "10.11.204.34";//电脑ip地址,在本地连接的属性下查找本机地址 private int port = 4652;//端口号 String[] imgStrings; BufferedReader reader; ExpandableListView exp; File file; boolean result = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_upload_photo); 查找组件img 、seriesUpload、filePath、exp。。。。 exp.setAdapter(getAdapter()); //点击子视图文本触发事件 exp.setOnChildClickListener(new OnChildClickListener() { @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { String nowString=img.getText().toString().concat(":"+getAdapter().getChild(groupPosition, childPosition).toString()); img.setText(nowString); exp.collapseGroup(0);//选择子文本后收缩下拉列表 return false; } }); //根据图片名称实现上传 mButton.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View v) { String strString = img.getText().toString(); imgStrings = new String[] {}; if (strString.trim().length() == 0) return; if (strString.contains(",")) {// 多张图片上传 imgStrings = strString.split(","); for (int i = 0; i < imgStrings.length; i++) { if(imgStrings[i].contains(":")) upload(imgStrings[i].split(":")[0],imgStrings[i].split(":")[1]); else upload(imgStrings[i], ""); getResult(i+1); } } else {//只传一张图片 if(strString.contains(":")){//选择图片描述 upload(strString.split(":")[0],strString.split(":")[1]); } else//没有选择图片描述 upload(strString, ""); getResult(1); } } }); //根据文件夹路径上传文件内一系列图片 seriesUpload.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View v) { String pathString = filePath.getText().toString(); imgStrings = new String[] {}; if (pathString.trim().length() == 0) return; String[] paths=listFile(filePath.getText().toString()); if (paths.length==0) {// 文件夹没有图片 Toast.makeText(UploadPhotoActivity.this, "文件夹内没有可传图片文件!", Toast.LENGTH_SHORT).show(); return; } else { for (int j = 0; j < paths.length; j++) { seriesUpload(paths[j]); getResult(j);//提示是否已上传成功 } } } }); } //i代表上传的图片索引,返回上传结果 private void getResult(int i) { if (file.length() != 0) Toast.makeText(UploadPhotoActivity.this, "上传成功!", Toast.LENGTH_SHORT).show(); else Toast.makeText(UploadPhotoActivity.this, "第"+i+"张上传失败,不存在此文件", Toast.LENGTH_SHORT).show(); } //得到文件夹下的所有图片绝对路径 public String[] listFile(String derect) { File file = new File(derect); File[] f = file.listFiles(); String Path[] = new String[f.length]; for (int i = 0; i < f.length; i++) { Path[i] = f[i].getPath(); System.out.println(Path[i]); } return Path; } // 根据图片名称上传照相机中单个照片 private void upload(String path,String scrip) { DataOutputStream dos; FileInputStream fis; try { ///sdcard/DCIM/Camera/照相机拍摄后图片所存路径 file = new File("/sdcard/DCIM/Camera/" + path.trim()); if (file.length() == 0) { return; } else { socket = new Socket(ip, port); dos = new DataOutputStream(socket.getOutputStream()); fis = new FileInputStream(file); dos.writeUTF(path.substring(0,path.indexOf("."))+scrip+path.substring(path.indexOf("."))); dos.flush(); byte[] sendBytes = new byte[1024 * 8]; int length; while ((length = fis.read(sendBytes, 0, sendBytes.length)) > 0) { dos.write(sendBytes, 0, length); dos.flush();// 发送给服务器 } dos.close();//在发送消息完之后一定关闭,否则服务端无法继续接收信息后处理,手机卡机 /*reader = new BufferedReader(new InputStreamReader( socket.getInputStream())); result = Boolean.parseBoolean(reader.readLine().toString()); System.out.println("上传结果" + result);//运行时总是提示socket关闭,不能接收服务端返回的消息 reader.close();*/ fis.close(); socket.close(); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); }catch (SocketTimeoutException e) { e.printStackTrace(); Toast.makeText(UploadPhotoActivity.this, "超时,上传失败", Toast.LENGTH_LONG).show(); }catch (IOException e) { e.printStackTrace(); } } //根据文件夹路径上传所有的图片到服务器 //此dirpath是图片绝对路径 private void seriesUpload(String dirpath) { DataOutputStream dos; FileInputStream fis; try { file = new File(dirpath); if (file.length() == 0) { return; } else { socket = new Socket(ip, port); dos = new DataOutputStream(socket.getOutputStream()); fis = new FileInputStream(file); dos.writeUTF(dirpath.substring(dirpath.lastIndexOf("/")+1));//截取图片名称 dos.flush(); byte[] sendBytes = new byte[1024 * 8]; int length; while ((length = fis.read(sendBytes, 0, sendBytes.length)) > 0) { dos.write(sendBytes, 0, length); dos.flush();// 发送给服务器 } dos.close();//在发送消息完之后一定关闭,否则服务端无法继续接收信息后处理,手机卡机 fis.close(); socket.close(); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); }catch (SocketTimeoutException e) { e.printStackTrace(); Toast.makeText(UploadPhotoActivity.this, "超时,上传失败", Toast.LENGTH_LONG).show(); }catch (IOException e) { e.printStackTrace(); } } //下拉列表适配器设置成自己需要的样式 private ExpandableListAdapter getAdapter(){ final String[] generalsTypes = new String[] { "图片分类"}; //子视图显示文字 final String[] generals = new String[] { "地方特色风景", "个人生活写照、旅游怀念","地图拍摄","美食", "工作需求图片", "办公环境写实","天气图片","幸福一家人", "好友、同事照片", "汽车","明星写真"}; //重写adapter类,创建新适配置 ExpandableListAdapter adapter=new ExpandableListAdapter(){ @Override public boolean areAllItemsEnabled() {return false;} @Override public Object getChild(int groupPosition, int childPosition) { return generals[childPosition]; } @Override public long getChildId(int groupPosition, int childPosition) { return childPosition; } @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { LinearLayout ll = new LinearLayout( UploadPhotoActivity.this); ll.setOrientation(0); TextView textView = new TextView(UploadPhotoActivity.this); AbsListView.LayoutParams lp = new AbsListView.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, 64); textView.setLayoutParams(lp); textView.setGravity(Gravity.CENTER_VERTICAL); textView.setPadding(36, 0, 0, 0); textView.setTextSize(18); textView.setText(getChild(groupPosition, childPosition) .toString()); ll.addView(textView); return ll; } @Override public int getChildrenCount(int groupPosition) {return generals.length;} @Override public Object getGroup(int groupPosition) {return generalsTypes[groupPosition];} @Override public int getGroupCount() {return generalsTypes.length;} @Override public long getGroupId(int groupPosition) {return groupPosition;} @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { LinearLayout ll = new LinearLayout( UploadPhotoActivity.this); ll.setOrientation(0); TextView textView = new TextView(UploadPhotoActivity.this); AbsListView.LayoutParams lp = new AbsListView.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, 50); textView.setLayoutParams(lp); textView.setGravity(Gravity.CENTER_VERTICAL); textView.setPadding(80, 5, 0, 0); textView.setTextSize(20); textView.setTextColor(Color.BLACK); textView.setText(getGroup(groupPosition).toString()); ll.addView(textView); return ll; } @Override public boolean hasStableIds() { return true; } @Override public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } @Override。。。。。。一系列要实现的父类的方法 }; return adapter; } }
4.创建服务器端,新建java-project,在main方法中写入服务器接收信息代码
服务端接收字节写入本地电脑文件内
//服务器
public class TransFileServer {
public static void main(String[] args) {
uploadThread t = new uploadThread();
t.start();
}
}
class uploadThread extends Thread {
private static final int HOST_PORT = 4652;
DataInputStream inputStream;
FileOutputStream fos;
boolean flag = false;
@Override
public void run() {
Socket skt = null;
try {
ServerSocket server = new ServerSocket(HOST_PORT);
while (true) {
skt = server.accept();
System.out.println("接收到Socket请求");
//接收客户端文件
inputStream = new DataInputStream(skt.getInputStream());
PrintWriter writer = new PrintWriter(skt.getOutputStream());
String trueName = inputStream.readUTF();
fos = new FileOutputStream("D://" + trueName);
byte[] inputByte = new byte[1024 * 8];
int length;
while ((length = inputStream.read(inputByte,0,inputByte.length)) > 0) {
System.out.println("正在接收数据..." + length);
flag = true;
fos.write(inputByte, 0, length);
fos.flush();
}
System.out.println("图片接收完成");
fos.close();
inputStream.close();
// 服务器发送消息
writer.println(flag);// 返回是否接收到图片
writer.flush();
writer.close();
skt.close();
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
5.可选择使用较完善的获取图片列表方法代码段:
public List<STRING> getPictures(final String strPath) { List<STRING> list = new ArrayList<STRING>(); File file = new File(strPath); File[] files = file.listFiles(); if (files == null) { return null; } for(int i = 0; i < files.length; i++) { final File f = files[i]; if(f.isFile()) { try{ int idx = f.getPath().lastIndexOf("."); if (idx <= 0) { continue; } String suffix = f.getPath().substring(idx); if (suffix.toLowerCase().equals(".jpg") || suffix.toLowerCase().equals(".jpeg") || suffix.toLowerCase().equals(".bmp") || suffix.toLowerCase().equals(".png") || suffix.toLowerCase().equals(".gif") ) { list.add(f.getPath()); } } catch(Exception e) { e.printStackTrace(); } } } return list; }
6.扩展功能;后期将介绍实现文件检索批量上传图片,用户通过Checkbox复选框选择自己要上传的图片(必须是图片文件才可上传,不是图片给用户提示信息),将这些选中的图片可获取图片名称加入到一个list数组保存起来,然后统一上传。