React-Native使用Nanohttpd實現跨平臺互傳文件

我使用的技術棧是:react-native(0.56.0)+ react-navigation + react-redux + ant-design + axios


我在做的ReactNative項目需要實現同一WIFI下跨平臺互傳文件功能,涉及原生模塊的功能

我實現這兩個功能使用的庫有


首先需要明白限制條件是在同一個局域網內(連接同一wifi),前期已經用umi開發了一組傳輸文件時準備在PC上展示的相關網頁並打包出來。

核心技術:

通過使用NanoHTTPD在手機上建立本地服務器實現數據傳輸。
爲了保證安全性,需要PC端輸入APP生成的驗證碼予以驗證。

整體流程:

1. 判斷是否連接WIFI並獲取ip地址

WifiManager是 Android 暴露給開發者使用的一個系統服務管理類, 其中包含對WiFi的響應的操作函數。

        //獲取wifi服務
        WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        //判斷wifi是否開啓
        if (!wifiManager.isWifiEnabled()) {
            return null;
        }
        //判斷wifi是否連接
        if (wifiManager.getConnectionInfo() == null) {
            return null;
        }
        // ip地址獲取
int ipAddress = wifiManager.getConnectionInfo().getIpAddress();
        return (ipAddress & 0xFF) + "." +
                ((ipAddress >> 8) & 0xFF) + "." +
                ((ipAddress >> 16) & 0xFF) + "." +
                (ipAddress >> 24 & 0xFF);

2. 啓動服務

主要邏輯繼承NanoHTTPD,根據你的需求重寫serve方法就可以了

在我寫的this.openHttp(port,code,mainKey,toPage)中,code是驗證碼,mainkey是自定義需要傳輸的部分內容,toPage是判斷是由手機向PC還是PC向手機傳輸文件。

// 隨機數防止端口被佔用,如果被佔用catch後stopServer
int port = (int)((Math.random()*9+1)*1000);
final String host = ip + ":" + port;
this.openHttp(port,code,mainKey,toPage);
promise.resolve("http://" + host);
// 
private void openHttp(int port,String code,ReadableMap mainKey, String toPage) throws IOException {
        httpServer = new HttpServer(getReactApplicationContext(), port,code,mainKey,toPage);
        httpServer.start();
    }

3. 自定義的serve,返回網頁或者響應請求

根據不同的路徑返回不同的數據

將文件轉化的字符串或者數組作爲響應內容返回return Response.newFixedLengthResponse(字符串)
或者return Response.newFixedLengthResponse(狀態碼,mime類型,字節數組)

public  Response serve(IHTTPSession session){
        String uri = session.getUri();
        // 根據html的路徑返回相應的操作
       if (uri.equals("/postValidatePhoneCode")) {
            return validatecode(session);
        }else if (uri.equals("/upload")){
            return renderUpload(session);
        }else if (uri.equals("/getPhoneKey")){
            return getKey(session);
        }else if (uri.equals("/device")){
            return download(session);
        }else if (uri.indexOf(".json")!=-1){
            return downloadKey(session);
        } else {
            return renderRes(session);
        }
    }

renderRes

不帶路徑的url根據toPage參數返回html,保證PC訪問到初始頁面

private Response renderRes(IHTTPSession session) {
        String uri = session.getUri();
        String filename;
        if (uri.equals("/")) filename = "index.html";
        else filename = uri.substring(1);     
// 定義mineType
        String mimeType = null; 
        if (filename.endsWith(".html") || filename.endsWith(".htm")) {
            mimeType = "text/html";
        } else if (filename.endsWith(".js")) {
            mimeType = "text/javascript";
        } else if (filename.endsWith(".css")) {
            mimeType = "text/css";
        }
        else if (filename.endsWith(".ico")) {
            mimeType = "image/x-icon";
        }
        if (mimeType == null) {
            return newFixedLengthResponse(Response.Status.BAD_REQUEST, "text/plain", "未知文件類型[" + filename + "]");
        }
        InputStreamReader inputStreamReader = null;
        BufferedReader reader = null;

        try {
//            filename = "Resources"+filename;
            inputStreamReader = new InputStreamReader(context.getAssets().open(filename));
            reader = new BufferedReader(inputStreamReader);

            StringBuilder returnMsg = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                returnMsg.append(line);
            }
            return newFixedLengthResponse(Response.Status.OK, mimeType, returnMsg.toString());

        } catch (IOException e) {
            Utils.closeSilent(reader);
            Utils.closeSilent(inputStreamReader);
            e.printStackTrace();
            return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, "text/plain", e.getMessage());
        }
    }

validatecode 驗證驗證碼

            session.parseBody(files);
            String param=files.get("postData");
            JSONObject json = new JSONObject(param);
            String code = json.getString("code");
            if (code.equals(this.code+"")){
                Map map = new HashMap();
                map.put("validate",true);
                map.put("toPage",this.toPage);
                JSONObject jsonObj=new JSONObject(map);
                return newFixedLengthResponse(Response.Status.OK, "text/plain", jsonObj.toString());
            }else {
                Map map = new HashMap();
                map.put("validate",false);
                JSONObject jsonObj=new JSONObject(map);
                return newFixedLengthResponse(Response.Status.REDIRECT_SEE_OTHER, "text/plain", jsonObj.toString());
            }

驗證碼正確則跳轉到相應頁面,進行下載或上傳

download 上傳文件到PC

private Response downloadKey(IHTTPSession session) {
        try {
            File distFile = new File(this.context.getFilesDir().getAbsolutePath() + File.separatorChar + "wifi" + File.separatorChar + this.mainKey.getString("key_name") + ".json");
            //判斷該文件的所屬文件夾存不存在,不存在則創建文件夾
            if(!distFile.getParentFile().exists()){
                distFile.getParentFile().mkdirs();
            }
            //創建文件
            if (!distFile.exists()){
                //如果不存在file文件,則創建
                distFile.createNewFile();
            }
            //創建字符流(使用字節流比較麻煩)
            FileWriter fw = new FileWriter(distFile);
            Map map = new HashMap();
            map.put("address",this.mainKey.getString("pubaddress"));
            map.put("algo","0x03");
            map.put("encrypted",this.mainKey.getString("privateKeystr").substring(2,this.mainKey.getString("privateKeystr").length()));
            map.put("version","2.0");
            map.put("privateKeyEncrypted",false);
            JSONObject jsonObj=new JSONObject(map);

            fw.write(jsonObj.toString());
            //這裏要說明一下,write方法是寫入緩存區,並沒有寫進file文件裏面,要使用flush方法才寫進去
            fw.flush();
            //關閉資源
            fw.close();
            FileInputStream fis = new FileInputStream(distFile.getPath());
            return newFixedLengthResponse(Response.Status.OK, "application/octet-stream", fis,fis.available());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return newFixedLengthResponse(Response.Status.BAD_REQUEST, "text/plain", e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            return newFixedLengthResponse(Response.Status.BAD_REQUEST, "text/plain", e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            return newFixedLengthResponse(Response.Status.BAD_REQUEST, "text/plain", e.getMessage());
        }

    }

upload 從PC端下載文件

private void sendUploadFileToClient(IHTTPSession session) throws IOException, ResponseException {

        Map<String, String> files = new HashMap<>();
        session.parseBody(files);

        Map<String, List<String>> parameters = session.getParameters();

        WritableArray params = Arguments.createArray();
        String str = "";
        for (String key : files.keySet()) {
            List<String> nameStr = parameters.get(key);
            if (nameStr == null) continue;
            File tempFile = new File(files.get(key));
            File distFile = new File(this.context.getFilesDir().getAbsolutePath() + File.separatorChar + "wifi" + File.separatorChar + tempFile.getName());

            Utils.copyFile(tempFile, distFile);//NanoHttpd 會自動在請求完畢刪除掉文件 所以克隆一份

            WritableMap map = Arguments.createMap();
            map.putString("name", nameStr.get(0));
            map.putString("path", distFile.getPath());
            params.pushMap(map);
            str = this.readTxt(distFile.getPath());
        }
// 頁面監聽WifiServer.FILE_UPLOAD_NEW事件,接收從PC導入的數據
this.context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(WifiServer.FILE_UPLOAD_NEW, str);
    }
    private  String readTxt(String filePath) {
        StringBuilder result = new StringBuilder();
        try {
            BufferedReader bfr = new BufferedReader(new InputStreamReader(new FileInputStream(new File(filePath)), "UTF-8"));
            String lineTxt = null;
            while ((lineTxt = bfr.readLine()) != null) {
                result.append(lineTxt).append("\n");
            }
            bfr.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result.toString();
    }

4. 關閉本地服務

this.httpServer.stop();

以上就是手機端起簡易服務器與PC端實現互傳的流程,涉及到很多java文件讀取的方法,我本來想使用nanohttpd的webserver解決,但是發現他只能展示頁面,不好修改(它本身就是依賴nanohttpd完成的,更多信息在nanohttpd的官方文檔)。讀了源碼也有些一知半解,最後在公司前輩的幫助下完成的該功能。

感謝閱讀,有疑問請留言,我會盡我所能解答的。(要源碼的就別留了)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章