文件读写两三事

  • 前言

    随意玩玩,顺便了解下io流。

  • 正文

    Java中存在File类,构造时候传入路径,即可锁定文件,如果不存在可以自己创建。

    读写文件的时候使用FileInputStream和FileOutputStream两个类,他们都是基于字节读取的。

    下面是一个demo

package FileDemo;

import java.io.File;

public class FileDemo1 {
    public static void main(String[] args) {
        File f = new File("/Users/MacBook/Downloads/mv.zip");
        System.out.println(f.exists());
        System.out.println(f.getAbsolutePath());
        System.out.println(f.length());
    }
}

    exist()判断文件是否存在,返回true或者false;

    getAbsolutePath()返回文件的绝对路径;

    length()返回的是long类型的文件长度,我的是64位系统,通过Long.Max_Value查看long的上限大概是2的63次方,它的长度计算起来大概是很大的GB数,所以大小完全不用担心。

    文件读写的demo

package FileDemo;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 文件读写demo
 * @author ctk
 *
 */
public class FileRWDemo {
    public static void main(String[] args) {
        File f= new File("src/FileDemo/hello.txt");//虚拟路径从项目下开始
        File nf = new File("src/FileDemo/temp.txt");
        long fsize = f.length();
        FileInputStream fins = null;
        FileOutputStream fous = null ;
        System.out.println(f.getAbsolutePath());
        System.out.println(fsize);
        try {
            if(!nf.exists())
                nf.createNewFile();
            fins = new FileInputStream(f);
            fous = new FileOutputStream(nf);
            long index = 0;
            int readSize = 20;

            while(index != fsize){
                if((fsize - index) < 20)
                    readSize = (int)(fsize - index);
                byte[] b = new byte[readSize];
                fins.read(b);
                System.out.println(new String(b));
                index = index + readSize;
            }

//            fous.write(b);
//            fous.write(b);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(fins != null)
                    fins.close();
                if(fous != null)
                    fous.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

    }
}

    我的读写模型是从文件读出了20个byte然后将20个字节写入新文件中,类似于滑动窗口的思路只是不做校验。文件指针使用int来记录,int的范围保证不会溢出的必要条件就是每次读取的byte数<Integer.Max_Value就可以了,使用index来记录主要是记录读取的进度,也可以考虑做成断点续传。

    一个断点续传的方法就是申请上传的时候把服务器的文件指针置于客户端index位置,“在通信时候双方协调成想要的效果”就是设计进程间通信的一个思路。而为了使得上传文件可用,必须加入校验index之前的文件是否与客户端的index文件一致,简称一致性校验。一致性校验的方法可以使逐个byte比较;如果为了减少通信成本,则可以使用md5这种类型的计算结果比较。

    综合这些细节,文件上传这个过程大概是:用户发起上传请求->服务器响应已准备好->用户发送文件名与大小->服务器检查在用户可操作空间内是否存在文件,没有则创建,完成工作后返回接受正文数据的ack->用户开始读取文件数据并写入上传通道中->...不断重复直到文件上传结束。断点续传可以是一个附加功能:如果上传的时候实现这个过程,则传给服务器文件指针并且做index前的md5校验和发送校验码,如果index属于合法范围(0,len),并且服务器存在该文件和md5的校验正确,则服务器可以从index位置开始写入数据。

    上传与下载其实是一个功能,明其一即可。

    然后回到文件读写,经过这次研究,我确信这些读写通道都十分的“懒”,如果你不flush或者不readfully,他们就会偷懒,但是只会在特定的情景有影响,比如在写的过程中使用size和byte的混合传输的时候。“懒”就是存在数据缓冲区,读的时候把任务订在哪儿了,但是这句代码执行完的时候却没有把任务为完成。

    下面是文件的一个demo

package FileDemo;

import java.io.File;
import java.io.IOException;
/**
 * 获取目录下的所有文件
 * @author ctk
 *
 */
public class FileDemo1 {
    public static void main(String[] args) throws IOException {
        File f = new File("/Users/MacBook/Downloads");
        File[] files = f.listFiles();
        for(int i=0;i<files.length;i++)
        {
            System.out.println("filesName:"+files[i].getName());
            System.out.println("isDir:"+files[i].isDirectory());
        }

    }
}

    listFiles这个方法返回此文件(目录)下所有的子文件,问我好用么?肯定好用撒,做linux在线命令行服务器的时候就用到了。

    此外,文件还有getParent,获取上级菜单,太方便了。

    对此功能的联想?做个在线存储空间可以吧?百度云盘之类的,如果是文件点击还可以做下载。为此必须确保在设计的时候多个连接为多个线程,并且线程独享一个“用户当前路径”,这个设计和linux的terminal的思路是一样的。你可以设计用户可访问空间,让它不能操作系统文件就行,一个在线存储服务器的模型大概就浮现在脑中了。

    下面是一个socket读写demo的片段

            if(f.exists()){
                FileInputStream fins = new FileInputStream(f);
                long fsize = f.length();
                int sendSize = 50000;
                long index = 0;
                double advance = 0;//进度
                ous.write((f.getName()+"\r\n").getBytes());//发送文件名
                LogFile.appendRecord("开始上传文件:"+f.getName());
                ous.flush();
                byte[] b = new byte[sendSize];
                while(index != fsize){
                    if(fsize - index < 50000){
                        sendSize = (int)(fsize - index);
                    }
                    System.out.println("发送长度:"+sendSize);
                    dous.writeInt(sendSize);//发送长度
                    dous.flush();
                    msg = ins.readLine();
                    System.out.println("收到信息:"+msg);
                    fins.read(b,0,sendSize);
                    System.out.println("发送数据");
                    dous.write(b,0,sendSize);//发送数据
                    dous.flush();

                    msg = ins.readLine();
                    System.out.println("收到信息:"+msg);
                    index = index + sendSize;
                    advance = (index*1.0)/(fsize);
                    System.out.println("进度:"+advance*100);
                }
                dous.writeInt(-1);//发送结束
                fins.close();
                LogFile.appendRecord("上传完成:"+f.getName());
            }
                    //创建路径
                    File dir = new File("src"+ServerSetup.separator+"file"+socket.getInetAddress().toString());
                    if(!dir.exists())
                        dir.mkdir();
                    String fileName = ins.readLine();
                    File f = new File(dir.getPath()+ServerSetup.separator+fileName);
                    FileOutputStream fos = null;
                    if(!f.exists())
                        f.createNewFile();
                    fos = new FileOutputStream(f);
                    DataInputStream dins = new DataInputStream(socket.getInputStream());
                    //上传接收
                    int size = 0;
                    int maxSize = 100000;
                    byte[] temp = new byte[maxSize];
                    while(true){
                        size = dins.readInt();
                        System.out.println("收到size:"+size);
                        ous.write(("长度-"+StatusCode.Ack.getName()).getBytes());
                        ous.flush();
                        if(size <= 0 || size >maxSize)
                        {
                            ous.write(-1);
                            ous.flush();
                            break;
                        }
                        dins.readFully(temp,0,size);
                        fos.write(temp,0,size);
                        fos.flush();

                        ous.write(("数据写入-"+StatusCode.Ack.getName()).getBytes());
                        ous.flush();
                    }
                    fos.close();
                    if(size == 0){
                        LogFile.appendRecord("文件不存在,客户端已取消连接");
                    }else if(size == -1)
                        LogFile.appendRecord("接受到文件:"+fileName);
                    else
                        LogFile.appendRecord("一次发送文件过长:"+size);

    最开始我使用的是read方法,它的“懒惰”使得这个程序在一次传输大byte的时候经常出现size出错,因为size+byte可能会在byte没读完的时候开始执行readInt方法,就读到了byte的范围内不是正确的size了,导致进程间不能很好的交流。所以为了避免“偷懒”我使用readFully方法,它可以一次性读满后执行下一句。

    另外文件读取流中,有一个参数可以设置末尾添加或者从头写入,意思就是初始化这个对象的时候指针实在0或者是在length位置。每一个读写流在生命开始的时候它就会有一个位置,你可以skip,在你执行程序的读的时候它会移动,到末尾了可以reset,但是读到哪里它就记录到哪里。

    下面是一个linux服务器的demo

package package3.server;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 文件命令服务器
 * @author ctk
 */
public class CommandServer {
    public static void main(String[] args) {
        ServerSocket server = null;
        try {
            server = new ServerSocket(9999);
            System.out.println("server setup successful!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        Socket client = null;
        BufferedReader ins = null;
        DataOutputStream dous = null;
        try {
            client = server.accept();
            ins = new BufferedReader(new InputStreamReader(client.getInputStream(),"UTF-8"));
            dous = new DataOutputStream(client.getOutputStream());
            while(true){
                String cmd = ins.readLine();
                System.out.println("server recv cmd:"+cmd);
                if(cmd.equals("hello"))
                {
                    dous.write("hello\r\n".getBytes());
                    dous.flush();
                }else if(cmd.equals("ls")){
                    File f = new File("/Users/MacBook/Downloads");
                    File[] files = f.listFiles();
                    dous.writeInt(files.length);
                    dous.flush();
                    for(int i=0;i<files.length;i++){
                        dous.write((files[i].getName()+"\r\n").getBytes());
                    }
                }else if(cmd.equals("bye")){
                    dous.write("bye\r\n".getBytes());
                    dous.flush();
                    break;
                }else if(cmd != null && cmd.equals("")){
                    continue;
                }else
                    dous.write("unknow cmd \r\n".getBytes());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try{
                dous.close();
                ins.close();
                client.close();
            }catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

    当输入ls的时候,将“当前目录”的所有子文件输出回去,不过这个demo只会固定输出一个目录,因为没做当前目录切换。

package package3.client;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 文件命令客户端
 * @author ctk
 */
public class CommandClient {
    public static void main(String[] args) {
        Socket client = null;
        try {
            client = new Socket("localhost",9999);
            System.out.println("connect server successful!");
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        DataInputStream dins = null;
        DataOutputStream dous = null;
        BufferedReader cmdReader =new BufferedReader(new InputStreamReader(System.in));
        try {
            dins = new DataInputStream(client.getInputStream());
            dous = new DataOutputStream(client.getOutputStream());
            BufferedReader bins = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8"));
            while(true){
                String cmd = cmdReader.readLine();
                if(cmd == null)
                    break;
                else if(cmd.equals(""))
                    continue;
                if(cmd.equals("ls")){
                    dous.write((cmd+"\r\n").getBytes());
                    int len = dins.readInt();
                    for(int i = 0;i<len;i++){
                        System.out.println(bins.readLine());
                    }
                }else
                {
                    dous.write((cmd+"\r\n").getBytes());
                    String msg = bins.readLine();
                    System.out.println("client recv :"+msg);
                }
            }

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                dins.close();
                dous.close();
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

    我的客户端是支持命令行输入的,不知道变成linux的插件有没有可能,好像Java这个方面会比较差,可能考虑使用Python或者C来做试试看。

    命令行服务器我的设想是匹配命令解析字符串并执行相应的功能,如果要像linux这样高效的话可能不是匹配字符串这个思路,或者它使用高效的字符串匹配算法,如KMP。

  • 后记

    闲暇时候需做些有趣的事情。

时间: 2024-11-29 00:07:54

文件读写两三事的相关文章

Node.JS 文件读写,把Sheet图集转换为龙骨动画图集

Node.JS 文件读写,把Sheet图集数据转换为龙骨动画图集数据 var fs = require("fs") var readline = require("readline"); var rl = readline.createInterface({ input:process.stdin, output:process.stdout }); var path = undefined; var dbName = undefined; rl.question(

Android中的文件读写全面总结

转载请注明出处:http://blog.csdn.net/bettarwang/article/details/41625187 在深入分析Java中的I/O类的特征及适用场合 一文中,我详细介绍了Java中的I/O,但是,如果以为Android中的I/O与Java中一样,那就大错特错了.实际上,它们有一定的相同之外,但更多的是区别,因为Android系统中的文件存放位置不同,读取方式也不一样.下面将详细介绍Android中的文件读写: 一.资源文件的读取,不需要在Manifest文件中添加权限

文件操作ofstream,open,close,ifstream,fin,按照行来读取数据, fstream,iosin iosout,fio.seekg(),文件写入和文件读写,文件拷贝和文件

 1.ofstream,open,close 写入文件 #include<iostream> #include<fstream> using namespace std; //通过ofstream的方式实现写入文件 open,close void main() { ofstream fout;  //ofstream输出文件 fout.open("E:\\1.txt");//打开文件 fout << "1234abcdef";

C语言文件读写操作总结

C语言文件操作 一.标准文件的读写 1.文件的打开 fopen() 文件的打开操作表示将给用户指定的文件在内存分配一个FILE结构区,并将该结构的指针返回给用户程序,以后用户程序就可用此FILE指针来实现对指定文件的存取操作了.当使用打开函数时,必须给出文件名.文件操作方式(读.写或读写),如果该文件名不存在,就意味着建立(只对写文件而言,对读文件则出错),并将文件指针指向文件开头.若已有一个同名文件存在,则删除该文件,若无同名文件,则建立该文件,并将文件指针指向文件开头. fopen(char

快速入门Python中文件读写IO是如何来操作外部数据的?

读写文件是最常见的IO操作.Python内置了读写文件的函数,用法和C是兼容的. 读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件). 读文件 要以读文件的模式打开一个文件对象,使用Python内置的open()函数,传入文件名和标示符 >>> f =

Python IO编程——文件读写

1.1   文件读写 1.1.1   读文件 >>> f = open('/root/python/hello.py','r')    #标识符r表示读 >>> f =open('/root/python/hello1.py', 'r')   #文件不存在报错 Traceback (most recent call last): File "<stdin>", line 1, in <module> FileNotFoundE

RandomAccessFile 文件读写中文乱码解决方案!

RandomAccessFile 读写文件时,不管文件中保存的数据编码格式是什么   使用 RandomAccessFile对象方法的 readLine() 都会将编码格式转换成 ISO-8859-1 所以 输出显示是还要在进行一次转码 例子: package fileReadAndWrite; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; /*

转载-Python学习笔记之文件读写

Python 文件读写 Python内置了读写文件的函数,用法和C是兼容的.本节介绍内容大致有:文件的打开/关闭.文件对象.文件的读写等. 本章节仅示例介绍 TXT 类型文档的读写,也就是最基础的文件读写,也需要注意编码问题:其他文件的读写及编码相关详见专题的博文. open()   close()     with open(...) as ... 看以下示例就能了解 Python 的 open() 及 close() 函数.这边调用 read()方法可以一次读取文件的全部内容,Python把

iOS-Senior1-数据处理(文件读写)

1.Model的数据层的理解       1.1沙盒基础 沙盒定义:每个iOS应用程序都会为自己创建一个文件系统目录(文件夹),这个独立,封闭,安全的空间,叫做沙盒. 沙盒特点: 沙盒的文件夹及各个文件夹的作用   查找沙盒的两个方式: 1.点击finder -> 前往(左上角)- > 按住alt键  -> 资源库 -> Developer -> CoreSimulator -> (目标文件夹) 2.搜索 -> 终端 -> 命令行: 显示:defaults