Java傳遞音頻給PC端C#程序(1)

需求

把Android手機(jī)的mp3文件以TCP的傳輸方式傳遞到Win10的C#程序上,并且附帶歌名等信息。
PC端的要求是傳遞一個(gè)序列化后的文件過去,里面包含了文件本體和文件名等信息。
該序列化(C#這里的序列化好像和Java里面相差甚大啊!起初我是一頭霧水的)后的文件格式如下:

QQ圖片20170317130027.png

( ⊙o⊙ )? 什么鬼,沒辦法硬著頭皮試,起初根本不知道這是什么碼,因?yàn)樵贏ndroid里,傳遞文件最底層的就是字節(jié)流,那這個(gè)又是什么編碼呢。

嘗試

方式有很多種,比如把所有信息轉(zhuǎn)成字節(jié)傳遞過去,但是告知對(duì)方在哪里是什么,這種很麻煩;把文件轉(zhuǎn)成byte[]后放入一個(gè)類中,該類攜帶歌曲信息以byte[]發(fā)送過去。
嘗試了幾種方法:

1、 第一次(失敗)

ObjectOutputSteam object = new ObjectOutputStream(socket.getOutputStream());

通過對(duì)象流發(fā)送,發(fā)送過去后直接亂碼了,不用說,肯定是不能通過這樣的方式發(fā)送了,因?yàn)閷?duì)象流是JVM之間的一種傳遞方式,把對(duì)象以這種方式傳遞明顯行不通。

2、 第二次(失敗)

直接利用文件流把文件轉(zhuǎn)成byte[]然后傳遞過去,但是這種傳遞沒有辦法傳遞歌曲信息,嘗試用類來包裹

byte[] musicBytes = getBytes(filePath);//歌曲文件的字節(jié)數(shù)組
SimpleMusicEntity simpleMusicEntity = new SimpleMusicEntity();
simpleMusicEntity.setMusicBytes(musicBytes);
simpleMusicEntity.setTitle(title);
    /**
     * 獲得指定文件的byte數(shù)組
     */
    public static byte[] getBytes(String filePath) {
        byte[] buffer = null;
        try {
            File file = new File(filePath);
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
            byte[] b = new byte[1024];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buffer;
    }

so,問題來了,我得到了一個(gè)類SimpleMusicEntity,里面富含了歌曲的所有信息(名稱、ext、文件字節(jié)),怎么傳呢。
用Gson轉(zhuǎn)對(duì)象為Json傳遞過去,成功了!就這么的,我測試了傳遞一曲“滴滴滴.mp3”成功了,對(duì)方從其中拿到byte[]數(shù)組轉(zhuǎn)文件也能播放成功。繼續(xù)測試,傳一首歌曲,直接OOM!錯(cuò)誤大概是消耗了幾十兆的空間導(dǎo)致App直接crash。

為什么呢,顯而易見,因?yàn)樽址L了!你把一個(gè)歌曲轉(zhuǎn)換成byte[]然后再轉(zhuǎn)換成String,你知道這個(gè)String.length()有多長么,不說了高興太早。

3、第三次

找了半天終于知道有這么一個(gè)東西“Base64”!
目前的做法是File轉(zhuǎn)byte[],byte[]通過轉(zhuǎn)為Base64的字符串,這樣得到的文件大小比原文件大1.2倍左右(拿首歌曲測試)

        byte[] musicBytes = getBytes(filePath);//歌曲文件的字節(jié)數(shù)組
        String str = new String(Base64.encode(musicBytes, Base64.NO_WRAP ));
        SimpleMusicEntity2 simpleMusicEntity2 = new SimpleMusicEntity2();
        simpleMusicEntity2.setMusicBytes(str);
        simpleMusicEntity2.setTitle(title);
        String string =simpleMusicEntity2.toString;
public class SimpleMusicEntity2{

    private String title;
    private String musicBytes;

    //構(gòu)造一個(gè)json字符串
    @Override
    public String toString() {
        return "{"
                + "\"title\":"
                + "\"" + title + '\"'
                + ","
                + "\"musicBytes\":"
                + "\"" + musicBytes + '\"' +
                '}'+
                "\r\n"
                ;
    }
}

Base64簡單說一下

上面第二行“Base64.NO_WRAP”
為什么要用這個(gè),因?yàn)槿绻肂ase64.DEFAULT的話,我們的string里會(huì)自動(dòng)添加換行符“\n”

flag常量

Base64.CRLF 這個(gè)參數(shù)意思是Win風(fēng)格的換行符,意思就是使用CR LF這一對(duì)作為一行的結(jié)尾而不是Unix風(fēng)格的LF
Base64.DEFAULT 這個(gè)參數(shù)是默認(rèn),使用默認(rèn)的方法來加密
Base64.NO_PADDING 這個(gè)參數(shù)是略去加密字符串最后的”=”
Base64.NO_WRAP 這個(gè)參數(shù)意思是略去所有的換行符(設(shè)置后CRLF就沒用了)
Base64.URL_SAFE 這個(gè)參數(shù)意思是加密時(shí)不使用對(duì)URL和文件名有特殊意義的字符來作為加密字符,具體就是以-和_取代+和/
//習(xí)慣上使用Base64.NO_WRAP,使用什么方式編碼就需要使用什么方式解碼。

Base64Api

//將字節(jié)數(shù)組編碼,返回為String
Base64.encodeToString(byte[] bs,int flag);
//將字節(jié)數(shù)組編碼,返回字節(jié)數(shù)組
Base64.encode(byte[] bs,int flag);
//將字節(jié)數(shù)組按指定位置部分編碼,返回字符串
Base64.encodeToString(byte[] bs,int offset,int lenth);
//將字節(jié)數(shù)組按指定位置部分編碼,返回字節(jié)數(shù)組
Base64.encode(byte[] bs,int offset,int lenth);
//將編碼后的字符串解碼返回字節(jié)數(shù)組
Base64.decode(String str,int flag);
//將編碼后的字節(jié)數(shù)組解碼返回字節(jié)數(shù)組
Base64.decode(byte[],int flag);
//將編碼后的字節(jié)數(shù)組按指定位置部分解碼,返回字節(jié)數(shù)組
Base64.decode(byte[] bs,int offset,int len);

最后

發(fā)送方式,下面兩種方式都行:

        byte[] bytes = string.getBytes();
        //byte[] enter = "\r\n".getBytes();//這個(gè)換行符必須寫在字符串末端,不能單獨(dú)發(fā)送換行符
        DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
        dos.write(bytes);
        //dos.write(enter);
        dos.flush();
        dos.close();
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bufferedWriter.write(string);
        bufferedWriter.flush();
        bufferedWriter.close();

終于PC端的C#程序能接收到文件信息,也能正確播放了,并且也有歌曲名字。

如果文件較大,可以先zip壓縮。如果還是較大,可以分段傳輸。

缺點(diǎn)

這種傳遞文件的做法不推薦,因?yàn)閭鬟f10M以上的文件極有可能OOM,因?yàn)镴ava虛擬機(jī)頂不住。

改進(jìn)

請(qǐng)看第二篇文章Java傳遞音頻給PC端C#程序(2)

這只是功能demo而已,其實(shí)Android還要考慮很多,比如ip是變化的,我怎么獲取對(duì)應(yīng)設(shè)備的ip,然后建立TCP連接等等。

喝水不忘挖井人

參考文獻(xiàn):
Android編碼解碼及其原理
文件與base64二進(jìn)制轉(zhuǎn)換
圖片與Base64相互轉(zhuǎn)換,c#與java通用
Byte[]和BASE64之間的轉(zhuǎn)換
對(duì)文件進(jìn)行base64編碼成字符串進(jìn)行保存或傳輸

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,778評(píng)論 18 399
  • 編碼問題一直困擾著開發(fā)人員,尤其在 Java 中更加明顯,因?yàn)?Java 是跨平臺(tái)語言,不同平臺(tái)之間編碼之間的切換...
    x360閱讀 2,515評(píng)論 1 20
  • tags:io categories:總結(jié) date: 2017-03-28 22:49:50 不僅僅在JAVA領(lǐng)...
    行徑行閱讀 2,220評(píng)論 0 3
  • 我希望,將來我結(jié)婚了,是因?yàn)閻矍椋皇且驗(yàn)闂l件合適或?yàn)榱朔毖芎蟠T谖铱磥恚星檎娴谋任镔|(zhì)重要很多,感情是點(diǎn)滴的...
    文藝小呆閱讀 272評(píng)論 0 0
  • 28/100 #100天堅(jiān)持一件事# 星期三 雨 早起6:00 早餐:小米紅棗粥 今日背誦經(jīng)文: 弟兄們,在心...
    zuimeideni閱讀 396評(píng)論 0 0