Java 文件操作工具类

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.io.FileUtils;

import com.bcap_systemlib.config.Configuration;

public class FileUtil {
    /**
     * 将多个文件打包到一个zip中
     *
     * @param sourceFolder
     * @param zipFile
     * @return
     * @throws Exception 
     */
    public static boolean zipFile(String sourceFolder, File zipFile) throws Exception{
        boolean isOk = true;
        File f = new File(sourceFolder);
        ZipOutputStream out = null;
        try{
            if(!f.exists()){
                f.mkdirs();
            }
            out = new ZipOutputStream(new FileOutputStream(zipFile));
            zip(out, f, "");
            out.flush();
            FileUtils.deleteDirectory(f);
        } catch (Exception e){
            e.printStackTrace();
            throw new Exception("压缩文件出错!");
        } finally
        {
            if(null != out){
                try{ out.close(); } catch (Exception e){ e.printStackTrace();}
            }
        }
        return isOk;
    }

    /**
     * 递归压缩文件
     * @param out
     * @param f
     * @param base
     * @throws Exception
     */
    private static void zip(ZipOutputStream out, File f, String base) throws Exception {
        if (f.isDirectory()) {
            File[] fl = f.listFiles();
            out.putNextEntry(new ZipEntry(base + "/"));
            base = base.length() == 0 ? "" : base + "/";
            for (int i = 0; i < fl.length; i++) {
                zip(out, fl[i], base + fl[i].getName());
            }
        }else {
            out.putNextEntry(new ZipEntry(base));
            FileInputStream in = new FileInputStream(f);
            int b;
            while ( (b = in.read()) != -1) {
                out.write(b);
            }
            in.close();
        }
    }

    /**
     * 下载单个文件
     *
     * @param file
     * @param request
     * @param response
     * @return
     */
    public static boolean downFile(File file, HttpServletRequest request, HttpServletResponse response) {
        boolean isOk = true;
        OutputStream myout = null;
        FileInputStream fis = null;
        BufferedInputStream buff = null;
        HttpSession session = request.getSession();
        if (session != null) {
            session.setAttribute("state", "");
        }
        try {
            response.setContentType("application/x-msdownload");
            response.setContentLength((int) file.length());
            response.setHeader("content-disposition", "attachment;filename=" + EncodingConvertUtil.utf2iso(file.getName()));
            fis = new FileInputStream(file);
            buff = new BufferedInputStream(fis);
            byte[] b = new byte[1024 * 10];//相当于我们的缓存
            long k = 0;//该值用于计算当前实际下载了多少字节
            //从response对象中得到输出流,准备下载
            myout = response.getOutputStream();
            while (k < file.length()) {
                int j = buff.read(b, 0, b.length);
                k += j;
                //将b中的数据写到客户端的内存
                myout.write(b, 0, j);
            }
            myout.flush();
        } catch (Exception e) {
            e.printStackTrace();
            isOk = false;
        } finally {
            try {
                if (null != myout) {
                    myout.close();
                    myout = null;
                }
                if (null != buff) {
                    buff.close();
                    buff = null;
                }
                if (null != fis) {
                    fis.close();
                    fis = null;
                }
                if(file.exists()){
                    FileUtil.delFile(file);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return isOk;
    }

    /**
     * 删除单个文件
     *
     * @param file
     * @return
     */
    public static boolean delFile(File file) {
        boolean isOk = true;
        try {
            if (file.isFile() && file.exists()) {
                file.delete();
            }
        } catch (Exception e) {
            e.printStackTrace();
            isOk = false;
        } finally {
            // log ...
        }
        return isOk;
    }
    
    public static void downloadFileFromRemote(String remoteFilePath, String localFilePath){
        URL urlfile = null;
        HttpURLConnection httpUrl = null;
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        File f = new File(localFilePath);
        try
        {
            urlfile = new URL(remoteFilePath);
            httpUrl = (HttpURLConnection)urlfile.openConnection();
            httpUrl.connect();
            bis = new BufferedInputStream(httpUrl.getInputStream());
            bos = new BufferedOutputStream(new FileOutputStream(f));
            int len = 2048;
            byte[] b = new byte[len];
            while ((len = bis.read(b)) != -1)
            {
                bos.write(b, 0, len);
            }
            System.out.println("下载成功");
            bos.flush();
            bis.close();
            httpUrl.disconnect();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            try
            {
                bis.close();
                bos.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }
    
    public static void writeFile(String filePathAndName, String fileContent) {
    	try {
    		File f = new File(filePathAndName);
    		if (!f.exists()) {
    			f.createNewFile();
    		}
    		OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(f),Configuration.SERVLET_CHARACTER_ENCODING);
    		BufferedWriter writer=new BufferedWriter(write);   
    		writer.write(fileContent);
    		writer.close();
    	} catch (Exception e) {
			System.out.println("写文件内容操作出错");
			e.printStackTrace();
    	}
    }
    
    public static String readFile(String filePathAndName) {
    	String fileContent = "";
    	try {  
    		File f = new File(filePathAndName);
    		if(f.isFile()&&f.exists()){
    			InputStreamReader read = new InputStreamReader(new FileInputStream(f),Configuration.SERVLET_CHARACTER_ENCODING);
    			BufferedReader reader=new BufferedReader(read);
    			String line;
    			while ((line = reader.readLine()) != null) {
    				fileContent += line;
    			}   
    			read.close();
    		}
    	} catch (Exception e) {
    		System.out.println("读取文件内容操作出错");
    		e.printStackTrace();
    	}
    	return fileContent;
    }
    
    public static String getExtName(String filename) {
        int index = filename.lastIndexOf(".");
 
        if (index == -1) {
            return null;
        }
        String result = filename.substring(index);
        return result;
    }
}

Java 文件操作工具类

flutter dart md5

flutter dart好像不像php那样,直接提供md5函数,因此从网上找了一段,记录一下

import 'dart:convert';
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';

class MD5{
  String generateMd5(String data) {
    var content = new Utf8Encoder().convert(data);
    var digest = md5.convert(content);
    return hex.encode(digest.bytes);
  }
}


flutter dart md5

flutter dart上的http请求类

网上找到了一个flutter dart请求类,但post部分有错误,已经做了修改,现贴出来

import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:blue_collar_age_class/utils/config.dart';

class HttpUtil {
  static HttpUtil? instance;
  Dio? dio;
  BaseOptions? options;

  CancelToken cancelToken = CancelToken();

  static HttpUtil? getInstance() {
    if (null == instance) instance = HttpUtil();
    return instance;
  }

  /*
   * config it and create
   */
  HttpUtil() {
    //BaseOptions、Options、RequestOptions 都可以配置参数,优先级别依次递增,且可以根据优先级别覆盖参数
    options = BaseOptions(
      //请求基地址,可以包含子路径
      baseUrl: Config.URL_BASE,
      //连接服务器超时时间,单位是毫秒.
      connectTimeout: 10000,
      //响应流上前后两次接受到数据的间隔,单位为毫秒。
      receiveTimeout: 5000,
      //Http请求头.
      headers: {
        //do something
        "version": "1.0.0"
      },
      //请求的Content-Type,默认值是"application/json; charset=utf-8",Headers.formUrlEncodedContentType会自动编码请求体.
      contentType: Headers.formUrlEncodedContentType,
      //表示期望以那种格式(方式)接受响应数据。接受四种类型 `json`, `stream`, `plain`, `bytes`. 默认值是 `json`,
      responseType: ResponseType.plain,
    );

    dio = Dio(options);

    //Cookie管理
    dio!.interceptors.add(CookieManager(CookieJar()));

    //添加拦截器
    dio!.interceptors
        .add(InterceptorsWrapper(onRequest: (RequestOptions options) {
      print("请求之前");
      // Do something before request is sent
      return options; //continue
    }, onResponse: (Response response) {
      print("响应之前");
      // Do something with response data
      return response; // continue
    }, onError: (DioError e) {
      print("错误之前");
      // Do something with response error
      return e; //continue
    }));
  }

  /*
   * get请求
   */
  get(url, {data, options, cancelToken}) async {
    Response? response;
    try {
      response = await dio!.get(url,
          queryParameters: data, options: options, cancelToken: cancelToken);
      print('get success---------${response.statusCode}');
      print('get success---------${response.data}');

//      response.data; 响应体
//      response.headers; 响应头
//      response.request; 请求体
//      response.statusCode; 状态码

    } on DioError catch (e) {
      print('get error---------$e');
      formatError(e);
    }
    return response;
  }

  /*
   * post请求
   */
  post(url, {data, params, options, cancelToken}) async {
    Response? response;
    try {
      response = await dio!.post(url, data:data, queryParameters: params, options: options, cancelToken: cancelToken);
      print('post success---------${response.data}');
    } on DioError catch (e) {
      print('post error---------$e');
      formatError(e);
    }
    return response;
  }

  /*
   * 下载文件
   */
  downloadFile(urlPath, savePath) async {
    Response? response;
    try {
      response = await dio!.download(urlPath, savePath,
          onReceiveProgress: (int count, int total) {
            //进度
            print("$count $total");
          });
      print('downloadFile success---------${response.data}');
    } on DioError catch (e) {
      print('downloadFile error---------$e');
      formatError(e);
    }
    return response!.data;
  }

  /*
   * error统一处理
   */
  void formatError(DioError e) {
    if (e.type == DioErrorType.CONNECT_TIMEOUT) {
      // It occurs when url is opened timeout.
      print("连接超时");
    } else if (e.type == DioErrorType.SEND_TIMEOUT) {
      // It occurs when url is sent timeout.
      print("请求超时");
    } else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
      //It occurs when receiving timeout
      print("响应超时");
    } else if (e.type == DioErrorType.RESPONSE) {
      // When the server response, but with a incorrect status, such as 404, 503...
      print("出现异常");
    } else if (e.type == DioErrorType.CANCEL) {
      // When the request is cancelled, dio will throw a error with this type.
      print("请求取消");
    } else {
      //DEFAULT Default error type, Some other Error. In this case, you can read the DioError.error if it is not null.
      print("未知错误");
    }
  }

  /*
   * 取消请求
   *
   * 同一个cancel token 可以用于多个请求,当一个cancel token取消时,所有使用该cancel token的请求都会被取消。
   * 所以参数可选
   */
  void cancelRequests(CancelToken token) {
    token.cancel("cancelled");
  }
}


flutter dart上的http请求类

AirTrack简介,及踩过的坑

为了使模拟飞行变得更加方便有趣,往往会使用一些外置设备,今天介绍一下“软外设”——AirTrack

AirTrack可以在苹果应用商店下载,228元,打开之后是下图的样子,需要去官网上下载电脑端插件,安装到X-Plane 11插件目录中,设定一下IP地址,当AirTrack和X- Plane 11处于同一局域网时,即可自动显示主机,点击主机连接。

默认的导航数据是2001版本的,很旧,最新版的又仅支持美国数据,想要最新全球数据,需要订阅navigraph,将其AirTrack的订阅码复制到本软件中,navigraph订阅208元/年(当前美元人民币汇率),下图是一些配置,主要是数值单位的选择,可以在国际单位和英制单位中切换。

按下CHARTS,可以查看航图,坑来了,默认是一些demo,还有本地航图,外部航图选项,外部航图要30元,我买了,买完之后才发现:US only,白白被坑¥30,如果需要航图的话,可以去网上找Jeppesen航图(for free),通过数据线同步到本软件中。

通讯这还是不错的,点击相应的内容,可以直接修改,省去了旋转旋钮的麻烦,尤其是在连飞活动中,这可以有效缓解手忙脚乱。

自动驾驶面板也不错,简单到777,复杂到737,全部功能都包含在里面了,这里说明一下,737中的LVL CHG和777中的FLCH是一样的。

然后是fmc,如果你用的是X-Plane 11自带的民航机,fmc信息会自动同步到这里,若是插件机,FF777,zibo 738之类的,则无法自动同步,想要使用此功能,需要先编辑好航路信息,或者用自带的机模将航路数据同步进来,然后再SAVE一下,再加载插件机。

最后就是仪表这,这个地方也是很细致,放大和缩小倍数那里比机模更细致。

总的来说,两个坑,一个是航路数据导入,一个是千万不要购买航图。


AirTrack简介,及踩过的坑

简单的连飞服务器搭建

据我了解,X-Plane 11只支持本地局域网联机,不支持公网联机,想要公网联机,通常需要在连飞平台上注册呼号,借助连飞客户端登录连飞平台,实现联机,常见的连飞平台有XFlySim,CFR(航空人生),VATSIM等,本文介绍,如何自己搭建连飞服务器,公网内网都可以用。支持X- Plane 11,FSX和P3D

准备材料

去github找到FSD代码,链接:https://github.com/sparrowhe/fsd

服务器:Linux

服务端编译运行

将代码包上传到Linux服务器并解压好,进入到CMakeLists.txt所在目录,使用cmake相关命令编译输出可执行程序fsd,编辑cert.txt,第一列是呼号,第二列是密码,此文件记录了连飞服务器的会员注册信息,执行./fsd启动服务器,别忘了放通相关端口(6809,3011,3010)。

这里面有个细节,就是文件编码,可以使用vim查看一下,默认应该是dos,需要用dos2unix工具转换成unix的。

安装客户端

下载连飞客户端xswift,及CSL机模映射包,安装好xswift后,根据提示一步一步配置,启动X-Plane 11,连接服务器,此时,多人可上线编队飞行

管制端

管制端软件使用EuroScope,安装好后,可以直接连接FSD服务器,管制扇区可以去VATSIM找。

结束

至此,能连飞,能管制的连飞系统搭建完成

本教程写的相当简单,起个提示作用,因为我相信,能有想法做连飞的,都不是吃干饭的。


简单的连飞服务器搭建

Jeppesen航图安装的坑

在飞行宝上找到Jeppesen的安装程序,资源详情页有安装教程,但安装时要注意,只能安装在C盘,但是又不想安装在C盘,怎么办呢?答:在D、E或者F盘创建Jeppesen的安装目录,创建快捷方式到C盘,这样在安装的时候,表面上看是安装到了C盘,实际安装到了其他盘

我是安装了N次才成功,第一次,安装完了打不开,百度告诉我需要管理员权限,给了管理员权限也不行,有人说不能用教育版本操作系统,又改成了家庭版,还是不行,最后按照默认路径安装到C盘成功了,但C盘满了,干脆卸掉,创建快捷方式后,重新安装。


Jeppesen航图安装的坑

X-Plane 11常用插件

AVS——机场视觉系统,起个实时定位作用,能方便的知道自己在哪,该怎么走

Airport-Navigator——机场导航,D版的AVS有时有BUG,无法正常使用,这个用起来不如AVS方便,但能在AVS失效时顶上

AviTab——飞行平板,能查航路,看地图,在B738中,能叫地勤

BetterPushback——后推车,用于飞机的后推

GndHandling——地勤插件

Little Xpconnect——连接little navmap,起连飞地图的作用

SAM——地面引导车,动态桥廊,等等

TerrainRadar——地形雷达

WebFMC——用手机平板控制FMC

X-Camera——视角切换

XPUIPC——连接SIMACARS,用于虚拟民航

xswiftbus——连接FSD服务器,用于连飞


X-Plane 11常用插件

FF777 插件机

FF777插件机初次启动只有很少的油,加油其他插件机复杂一些,打开菜单,设定乘客数量、行李重、APU时间、滑行时间、起飞油量,打开Doors MAN、舷梯、油车、电车、轮挡、打开全部机舱门,点击LOAD FUEL开始加油,等待加油完成即可,不支持空中修改油量。

相比于B738,没有了导航频率和此航线的旋钮,盲降设置在FMC的NAV RAD(导航雷达)里面了,在ILS-MLS处设置导航台频率和磁航向。

俯仰配评轮没了,鼠标也不好使,需要使用快捷键,襟翼和扰流板也需要快捷键。

自动驾驶面板中,FLCH相当于B738的LVL CHG,A/P是CMD,SEL都设在了旋钮中心

FF777自动化程度很高,会B738之后,这个几乎是零难度上手


FF777 插件机