Socket实现文件互传(一)

        最近一直在做比赛的一个项目,实现客户端和PC端的文件互传,其实一开始在看到这个题目的时候,完全不知道怎么去实现,感觉一脸懵逼,后来在查阅了资料以及相关书籍后了解到可以用Socket来进行通信,通过IO流来实现文件的互传,于是开始着手写这个项目。下面来详细介绍

一.   读取手机文件资源。

要传输文件首先要有文件可传,这就要先从手机数据库中读取各种文件资源,包括音乐、视频、图片、文档、压缩包、以及应用安装包。当然我只是列举了一部分常用的,还有很多类型,但是并不是很常用,所以就没有列举出来。

1.音乐

获取手机音乐其实并不难,Android已经将这些都封装好了,手机数据库中音乐的地址是MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;所以直接在这个uri下查找就可以了。附上代码:

public Cursor loadMusic(ContentResolvercontentResolver){
       String musicSort = MediaStore.Audio.Media.TITLE;
       Cursor musicCursor = contentResolver.query(FileFragment.musicUri, null, null, null,musicSort);
       if(musicCursor != null){
           while(musicCursor.moveToNext()){
               String title =musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
               String artist =musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
               String size =musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.SIZE));
               String path =musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.DATA));
               MediaFiles song = new MediaFiles();
               song.setFileName(title);
               song.setFileSize(size);
               song.setFilePath(path);
               song.setArtist(artist);
               musicList.add(song);
            }
        }
        return musicCursor;
}

这样就可以把手机中的音乐文件都包装成添MediaFiles对象加到musicList这个集合中了,其中MediaFiles是我写的一个文件类,包括了文件的一些常用属性,通过title获取到音乐文件的标题,artist获取的是歌手名,size表示音乐文件的大小,path为绝对路径。然后把这些信息分别赋给MediaFiles这个文件对象的对应属性。附上代码:

import android.widget.ImageView;
import java.io.File;
public class MediaFiles{
 
    public intfileImage;
    public String fileSize;
    public String fileName;
    public String artist;
    public String filePath;
    public ImageView check;
    public int count = 0;
    public boolean isFile = false;
 
    public void setFileImage(int fileImage){
       this.fileImage = fileImage;
    }
 
    public void setFileSize(String fileSize){
       this.fileSize = fileSize;
    }
 
    public void setFileName(String fileName){
       this.fileName = fileName;
    }
 
    public void setArtist(String artist){
       this.artist = artist;
    }
 
    public void setFilePath(String filePath){
       this.filePath = filePath;
    }
 
    public int getFileImage(){
        returnfileImage;
    }
 
    public String getFileSize(){
        returnfileSize;
    }
 
    public String getFileName(){
        returnfileName;
    }
 
    public String getArtist(){
        returnartist;
    }
 
    public String getFilePath(){
        returnfilePath;
}

这样获取手机的音乐资源就完成了!         附上图片

Socket实现文件互传(一)_第1张图片

2.视频

和获取音乐资源一样,视频文件还是通过一个uri来在手机数据库中查询的。Uri videoUri =MediaStore.Video.Media.EXTERNAL_CONTENT_URI; 所以获取视频文件就照猫画虎了。附上代码:

public Cursor loadVideo(ContentResolvercontentResolver){
       String videoSort = MediaStore.Video.Media.DISPLAY_NAME;
       Cursor videoCursor = contentResolver.query(FileFragment.videoUri, null, null, null,videoSort);
       if(videoCursor != null){
           while(videoCursor.moveToNext()){
               String title = videoCursor.getString(videoCursor.getColumnIndex(MediaStore.Video.Media.DISPLAY_NAME));
               String size =videoCursor.getString(videoCursor.getColumnIndex(MediaStore.Video.Media.SIZE));
               String path = videoCursor.getString(videoCursor.getColumnIndex(MediaStore.Video.Media.DATA));
               MediaFiles video = new MediaFiles();
               video.setFileName(title);
               video.setFileSize(size);
               video.setFilePath(path);
               videoList.add(video);
            }
        }
        return videoCursor;
}

Title、文件标题,size、文件大小,path、文件路径。全部包装成MediaFiles对象,添加到videoList这个集合中,这样视频文件也就搞定了!附上图片:

Socket实现文件互传(一)_第2张图片 视频只有这一个,所以看起来就不是很爽。。。

3.图片

图片文件还是一样的套路,都是套路,图片的uri : MediaStore.Images.Media.EXTERNAL_CONTENT_URI;只不过在处理图片文件的时候我并没有把全部文件都添加到一个集合当中,因为这样看起来不是很直观,而是通过文件夹来读取的,如果图片所在的文件夹都相同,就把这些图片放到一个集合当中,这样看起来就很清楚了。附上代码:

public Cursor loadImage(ContentResolver contentResolver){
        String imageSort = MediaStore.Images.Media.TITLE;
        Cursor imageCursor = contentResolver.query(FileFragment.imageUri, null, null, null, imageSort);
        if(imageCursor != null){
            while(imageCursor.moveToNext()){
                String path = imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
                File file = new File(path);
                if(file.exists()) {
                    File parentFile = new File(path).getParentFile();
                    if (parentFile == null) {
                        continue;
                    }
                    String folderPath = parentFile.getAbsolutePath();
                    if (!parentPath.contains(folderPath)) {
                        parentPath.add(folderPath);
                        ImageFolder imageFolder = new ImageFolder();
                        imageFolder.setFolderPath(folderPath);
                        imageFolder.setFirstPath(path);
                        imageFolder.setCount(getFileCount(folderPath));
                        imageFolders.add(imageFolder);
                    }
                }
            }
            parentPath = null;
        }
        return imageCursor;
}

首先获取到一张图片文件,如果该图片文件对应的文件夹不为空,并且保存文件夹路径的集合不包括该文件夹的路径,就给这个文件夹设置它的路径,第一张图片的路径,文件夹下图片的个数,并把这个文件夹对象添加到保存文件夹对象的集合当中。这样图片的处理就完成了(下面附上ImageFolder类和getFileCount() 获取文件夹下图片个数的代码)。比起音乐和视频,图片还是有那么一点复杂,但是只要思路清楚,其实问题也并不大。

public class ImageFolder {

    /**
     * 图片文件夹路径
     */
    private String folderPath;

    /**
     * 第一张图片的路径
     */
    private String firstPath;

    /**
     * 图片数量
     */
    private int count;

    public void setFolderPath(String folderPath){
        this.folderPath = folderPath;
    }

    public void setFirstPath(String firstPath){
        this.firstPath = firstPath;
    }

    public void setCount(int count){
        this.count = count;
    }

    public String getFolderPath(){
        return folderPath;
    }

    public String getFirstPath(){
        return firstPath;
    }

    public int getCount(){
        return count;
    }

}
/**
 * 获取对应路径下图片的个数
 */
    public int getFileCount(String path){
        int count = 0;
        File [] files = new File(path).listFiles();
        if(files == null){
            return 0;
        }
        for (File file : files) {
            if (file.isFile() && (file.getAbsolutePath().endsWith("jpg")
                    || file.getAbsolutePath().endsWith(".png")
                    || file.getAbsolutePath().endsWith("gif")
                    || file.getAbsolutePath().endsWith("jpeg"))) {
                count++;
            }
        }
        return count;
} 

附上图片

Socket实现文件互传(一)_第3张图片        Socket实现文件互传(一)_第4张图片

4.文档

        对于文档来说,Android并没有提供直接在数据库中查找的方法,这就比较麻烦了,不过这也难不住我们,可以通过遍历手机存储空间来查找到我们想要的文件资源,不过这样会有点慢,但是总体还是可行的。附上代码:

public List loadFiles (String path, String suffix, boolean isIterative){
        File [] files = new File(path).listFiles();
        for (File file : files) {
            if (file.isFile()){
                if(file.getPath().endsWith(suffix)) {
                    MediaFiles mediaFiles = new MediaFiles();
                    mediaFiles.setFileName(file.getName());
                    mediaFiles.setFilePath(file.getPath());
                    try {
                        FileInputStream fis = new FileInputStream(file);
                        mediaFiles.setFileSize(String.valueOf(fis.available()));          //通过IO流获取文件大小
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    filesList.add(mediaFiles);
                }
                if (!isIterative) {
                    break;
                }
            } else if(file.isDirectory()) {
                loadFiles(file.getPath(), suffix, isIterative);
            }
        }
        return filesList;
}

这里我写了一个查找后缀名的文件的方法,有三个参数,查找路径,文件后缀名,是否继续查找(一般为true)。其中查找路径就是手机存储空间路径Environment.getExternalStorageDirectory() +"/",这个没什么好说的,文件后缀名就是要查找那种类型的文件,这里我们递归来查找文件,得到根目录下的所有文件后,然后遍历,如果是文件就判断是否为我们想要的文件,如果是文件夹,就递归调用该方法继续查找。这样我们就可以把对应后缀名的文档资源获取到一个集合中。其中有一个需要注意的就是获取文件大小,File类并没有提供获取文件大小的方法,这里我们用IO流来处理,给对应文件对象搭一个输入流FileInputStream(),然后用这个流的available()方法就可以得到文件的大小了,不过单位是字节,需要对其进行转换,看起来更直观。

文档的类型我总共获取了doc,xlsx,pptx,txt,zip,这几种类型,这都是比较常用的。

附上图片:

Socket实现文件互传(一)_第5张图片Socket实现文件互传(一)_第6张图片

5.访问内部存储空间

       内部存储空间的路径就是我们在访问文档时的路径,即Environment.getExternalStorageDirectory() +"/",仔细看这个方法ExternalStorage,意思是外部存储,但是很多手机厂商在区分这些的时候,把手机自身的存储空间划分到了这个区域,而SD卡的路径一般都在storage/sdcard1或者storage/ext_sdcard下面,我们可以先判断手机SD卡是否可用,

/**
 * 判断SD卡是否可用
 */
    public Object externalStorageAvailable(){
        File file = Environment.getExternalStorageDirectory().getParentFile().getParentFile();
        File [] files = file.listFiles();
        for (File file1:files) {
            if(file1.getName().contains("ext_sdcard")
                    || file1.getName().contains("sdcard1")
                        || file1.getName().contains("sdcard2")){
                externalPath = file1.getAbsolutePath();
                return file1.getAbsolutePath();
            }
        }
        return null;
}

如果SD卡不可用就返回空,否则就得到SD卡的目录。然后我们就能得到内部存储和SD卡下的所有文件。附上代码;

/**
 * 获取对应路径下的所有文件
 */
    public List loadStorage(String path){
        File [] files = new File(path).listFiles();
        for(File file : files){
            MediaFiles mediaFiles = new MediaFiles();
            mediaFiles.setFileName(file.getName());
            mediaFiles.setFilePath(file.getPath());
            if(file.isFile()){
                try {
                    FileInputStream fis = new FileInputStream(file);
                    mediaFiles.setFileSize(String.valueOf(fis.available()));          //获取文件大小
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                mediaFiles.isFile = true;
            }
            storage.add(mediaFiles);
        }
        return storage;
    }

跟读取文档一样,先给这个方法传一个路径(内部存储或SD卡)再是通过file.listFiles()方法获取到该目录文件下的所有文件,然后把name,path,size三个属性赋给MediaFiles对象。再把这个对象添加到集合中,最后返回该集合。这样就可以得到所有存储空间下的所有文件了。还有一个要注意的就是我们在得到了存储空间下的所有文件后,我们要继续访问它们的子目录,一层层的深入,这就要给显示file的listView设置监听,如果当前是文件夹就继续显示它的字目录下的所有文件。附上代码:

public class ListListener implements AdapterView.OnItemClickListener{

        @Override
        public void onItemClick(AdapterView parent, View view, int position, long id) {
            edit.setVisibility(View.GONE);
            MediaFiles file = loadFile.getStorage().get(position);
            if(!file.isFile){
                MediaFiles files = loadFile.getStorage().get(0);
                path = files.getFilePath();         //保存当前第一个元素的路径
                loadFile.getStorage().clear();
                loadFile.loadStorage(file.getFilePath() + "/");
                StorageAdapter adapter = new StorageAdapter(getApplicationContext(), loadFile.getStorage());
                listView.setAdapter(adapter);
                listView.setOnItemClickListener(new ListListener());
            }
        }
    }

获取到当前文件夹的路径,然后用storage()方法获取当前文件夹下的所有文件,然后再用listView显示出来。这样就能把所有文件都显示出来了。 

现在我们已经可以访问到存储空间下的各级文件了,但是如果我们想返回到上一级目录,结果直接返回到了上一个界面,所以就要重写activity的onKeyDown()方法,

附上代码:

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if(keyCode == KeyEvent.KEYCODE_BACK){
            List lastFiles;
            if(loadFile.getStorage().isEmpty()){
                lastFiles = loadFile.loadStorage(path.substring(0, path.lastIndexOf('/')));
            } else {
                MediaFiles file1 = loadFile.getStorage().get(0);
                MediaFiles file2 = new LoadFile(Storage.this).loadStorage(Environment.getExternalStorageDirectory() + "/").get(0);
                if (file1.getFilePath().equals(file2.getFilePath())) {
                    finish();
                    return true;
                } else {
                    lastFiles = loadFile.getLastFile(loadFile.getStorage().get(0));
                }
            }
            StorageAdapter adapter = new StorageAdapter(getApplicationContext(), lastFiles);
            listView.setAdapter(adapter);
            listView.setOnItemClickListener(new ListListener());
        }
        return false;
    }

首先我们判断当前目录是否为空,如果为空,我们在上一段代码中有一段粗体代码,MediaFilesfiles = loadFile.getStorage().get(0); path =files.getFilePath();         //保存当前第一个元素的路径,也就是当前目录的上一级目录的第一个文件,我们通过path.substring(0, path.lastIndexOf('/'))方法得到包含上一级目录所有文件的文件夹的路径,这样显示这个path下的所有文件就可以了。如果当前目录不为空,先获取到当前目录下的第一个文件file1,然后拿到第一级目录下的第一个文件file2,如果file1。getPath() == file2.getPath()就表示当前目录是第一级目录,然后直接finish当前activity,否则就用getLastFiles()得到上一级目录的所有文件,

/**
 * 获取上一级目录的全部文件
 */
    public List getLastFile(MediaFiles file){
        String path = file.getFilePath();
        File aFile = new File(path).getParentFile().getParentFile();
        File [] files = aFile.listFiles();
        storage.clear();
        for(File file1:files){
            MediaFiles mediaFiles = new MediaFiles();
            mediaFiles.setFilePath(file1.getPath());
            mediaFiles.setFileName(file1.getName());
            if(file1.isFile()){
                try {
                    FileInputStream fis = new FileInputStream(file1);
                    mediaFiles.setFileSize(String.valueOf(fis.available()));          //获取文件大小
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                mediaFiles.isFile = true;
            }
            storage.add(mediaFiles);
        }
        return storage;
    }

得到该文件的父文件的父文件,然后遍历该目录下的所有文件并添加到集合中,也就得到了该文件的上一级目录下的所有文件。

至此我们就完成了手机常用文件的获取以及内部存储以及SD卡的所有目录下文件的访问。

 附上存储目录图片以及文件管理主界面:

Socket实现文件互传(一)_第7张图片         Socket实现文件互传(一)_第8张图片

附上文件操作的代码:

    /**
     * 复制文件
     */
    public int copyFiles(String sourcePath, String targetPath){

        File file1 = new File(sourcePath);
        if(file1.isFile() && file1.exists()){
            try {
                File file = new File(targetPath);
                InputStream fis = new FileInputStream(sourcePath);
                OutputStream fos = new FileOutputStream(file.getParent() + '/' + file1.getName());
                byte [] b = new byte[1024];
                int c;
                while((c = fis.read(b)) > 0){
                    fos.write(b, 0, c);
                }
                fos.close();
                fis.close();
                return 1;
            } catch (IOException e) {
                Log.e("e", e.toString());
                return -1;
            }
        } else {
            File file2 = new File(targetPath + '/' + file1.getName());
            if(!file2.exists()) {
                file2.mkdirs();
            } else {
                return 0;
            }
            File [] files = file1.listFiles();
            if(files.length == 0){
                return 1;
            } else {
                for (File file : files) {
                    copyFiles(file.getAbsolutePath(), file2.getPath());
                }
                return 1;
            }
        }
    }

    /**
     * 删除文件
     */
    public boolean deleteFile(File file) {
        if (!file.exists()) {
            return false;
        } else {
            if (file.isFile() && file.exists()) {
                return file.delete();
            }
            if (file.isDirectory()) {
                File[] childFile = file.listFiles();
                if (childFile == null || childFile.length == 0) {
                    return file.delete();
                }
                for (File f : childFile) {
                    deleteFile(f);
                }
                return file.delete();
            }
        }
        return false;
    }
 
    /**
     * 移动文件
     */
    public int moveFile(String oldPath, String newPath){
        File oldFile = new File(oldPath);
        if(!oldFile.exists()){
            return 0;
        }
        File newFile = new File(newPath);
        if(!newFile.exists()){
            newFile.mkdirs();
        }
        if(oldFile.isFile()){
            oldFile.renameTo(new File(newPath + File.separator + oldFile.getName()));
            return 1;
        } else if(oldFile.isDirectory()){
            File [] sourceFile = oldFile.listFiles();
            for(File file : sourceFile){
                if(file.isFile()){
                    oldFile.renameTo(new File(newPath + File.separator + file.getName()));
                }
                if(file.isDirectory()){
                    moveFile(file.getAbsolutePath(), newFile.getAbsolutePath() + File.separator + file.getName());
                }
            }
            return 1;
        }
        return 0;
    }

现在用socket实现文件传输的前期工作已经完成了,接下来就是文件传输的部分了,这部分内容我会在下篇博客中详细介绍!!!


你可能感兴趣的:(Android)