日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

bsdiff php,Apk差分升级Android客户端和Node.js服务端实现

發布時間:2025/3/21 php 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 bsdiff php,Apk差分升级Android客户端和Node.js服务端实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

核心的內容是bsdiff和bspatch

源碼根目錄/bootable/recovery/applypatch下找到,bsdiff官網同樣也是可以的,編出來的二進制文件可以在源碼根目錄out/host/linux-x86/bin下找到

bsdiff

bsdiff oldfile newfile patchfile

bspatch

bspatch oldfile newfile patchfile

比如用ES文件瀏覽器的3.1.7.1和3.1.8版本來做例子

./bsdiff es_3.1.7.1.apk es_3.1.8.apk diff.patch

./bspatch es_3.1.7.1.apk es_file.apk diff.patch

然后查看生成的es新的apk和最新版本的md5比較,發現一模一樣,說明成功

md5sum es_3.1.8.apk

c67a6ceb5c2da587da352be9b226a5df es_3.1.8.apk

md5sum es_file.apk

c67a6ceb5c2da587da352be9b226a5df es_file.apk

這樣我們Android客戶端只需要對bspatch封裝一個jni的庫出來就可以通過舊的apk和差分文件合成新的apk,然后進行安裝升級

移植過程:

1.下載bsdiff源文件

http://www.daemonology.net/bsdiff/,目前最新為4.3,下載下來以后發現里面包含這些文件

├── bsdiff.1

├── bsdiff.c

├── bspatch.1

├── bspatch.c

└── Makefile

我們客戶端sdk需要的是bspatch.c

創建jni文件夾,拷貝bspatch.c文件,創建Android.mk文件

目錄如下:

jni/

├── Android.mk

└── bsdiff

└── bspatch.c

Android.mk的內容如下:

LOCAL_PATH?:=?$(call?my-dir)

include?$(CLEAR_VARS)

bsdiff?:=?bsdiff

LOCAL_C_INCLUDES?+=?\

$(LOCAL_PATH)/$(bsdiff)/

bsdiff_source_list?:=?$(wildcard?$(LOCAL_PATH)/$(bsdiff)/*.c)

my_source_list?:=?$(wildcard?$(LOCAL_PATH)/*.c)

LOCAL_SRC_FILES?+=?\

$(bsdiff_source_list:$(LOCAL_PATH)/%=%)?\

$(my_source_list:$(LOCAL_PATH)/%=%)

LOCAL_MODULE?:=?bspatch

LOCAL_LDLIBS?:=??-llog

include?$(BUILD_SHARED_LIBRARY)

ndk-build編譯一下試試火氣,我去,果然不出所尿

jni/bsdiff/bspatch.c:31:19:?fatal?error:?bzlib.h:?No?such?file?or?directory

原來差bzlib

趕緊的下

此為搜索bzlib官網和下載解壓時間

============================================================================================================

============================================================================================================

好了,下載下來了(http://www.bzip.org/)

解壓發現文件好多,是不是都需要咧,慢慢來試,反正bzlib.h bzlib.c肯定需要,先創建bzlib文件夾,拷貝這兩個文件進去,對應修改Android.mk

LOCAL_PATH?:=?$(call?my-dir)

include?$(CLEAR_VARS)

bzlib?:=?bzlib

bsdiff?:=?bsdiff

LOCAL_C_INCLUDES?+=?\

$(LOCAL_PATH)/$(bzlib)/?\

$(LOCAL_PATH)/$(bsdiff)/

bzlib_source_list?:=?$(wildcard?$(LOCAL_PATH)/$(bzlib)/*.c)

bsdiff_source_list?:=?$(wildcard?$(LOCAL_PATH)/$(bsdiff)/*.c)

my_source_list?:=?$(wildcard?$(LOCAL_PATH)/*.c)

LOCAL_SRC_FILES?+=?\

$(bzlib_source_list:$(LOCAL_PATH)/%=%)?\

$(bsdiff_source_list:$(LOCAL_PATH)/%=%)?\

$(my_source_list:$(LOCAL_PATH)/%=%)

LOCAL_MODULE?:=?bspatch

LOCAL_LDLIBS?:=??-llog

include?$(BUILD_SHARED_LIBRARY)

ndk-build一下,不出所尿,肯定會出錯。。。。。。。。。。。

果然:

jni/bzlib/bzlib.c:31:27:?fatal?error:?bzlib_private.h:?No?such?file?or?directory

繼續坑。。。。

差哪坨加哪坨

最后加到一定劑量終于ok,bzlib最后定稿的樣子

jni/bzlib/

├── blocksort.c

├── bzlib.c

├── bzlib.h

├── bzlib_private.h

├── compress.c

├── crctable.c

├── decompress.c

├── huffman.c

└── randtable.c

下面開始寫jni調用bspatch

根據需求寫了一個工具類

packagecom.cvte.update.utils;

publicclassDeltaUpdate?{

publicstaticnativevoidbspatch(String?oldFilePath,?String?newFilePath,?String?patchFilePath);

static{

System.loadLibrary("bspatch");

}

}

然后用javah -jni 生成jni頭文件和源文件

cd bin/classes

執行

javah?-jni?com.cvte.update.utils.DeltaUpdate會生成com_cvte_update_utils_DeltaUpdate.h文件

移動到jni目錄

對應編寫com_cvte_update_utils_DeltaUpdate.c文件

#include?"com_cvte_update_utils_DeltaUpdate.h"

JNIEXPORT?voidJNICALL?Java_com_cvte_update_utils_DeltaUpdate_bspatch

(JNIEnv?*env,?jclass?jcls,?jstring?oldApkPath,?jstring?newApkPath,?jstring?patchFilePath)?{

constchar*old_apk_path?=?(*env)->GetStringUTFChars(env,?oldApkPath,?NULL);

constchar*new_apk_path?=?(*env)->GetStringUTFChars(env,?newApkPath,?NULL);

constchar*patch_file_path?=?(*env)->GetStringUTFChars(env,?patchFilePath,?NULL);

//?do?bspatch

(*env)->ReleaseStringUTFChars(env,?oldApkPath,?old_apk_path);

(*env)->ReleaseStringUTFChars(env,?newApkPath,?new_apk_path);

(*env)->ReleaseStringUTFChars(env,?patchFilePath,?patch_file_path);

}

基本成形,就差核心實現

jni庫移植的做法其實很簡單:就是把main函數改為jni的調用

找到

bspatch.c的main函數,把命令行傳過來的argv[]參數換為我們jni傳遞過來的參數就可以了

修改效果如下:

intbspatch(constchar*old_apk_path,constchar*new_apk_path,constchar*patch_file_path)?{

FILE*?f,?*?cpf,?*?dpf,?*?epf;

BZFILE?*?cpfbz2,?*?dpfbz2,?*?epfbz2;

intcbz2err,?dbz2err,?ebz2err;

intfd;

ssize_t?oldsize,newsize;

ssize_t?bzctrllen,bzdatalen;

u_char?header[32],buf[8];

u_char?*old,?*new;

off_t?oldpos,newpos;

off_t?ctrl[3];

off_t?lenread;

off_t?i;

/*?Open?patch?file?*/

if((f?=?fopen(patch_file_path,"r"))?==?NULL)

err(1,?"fopen(%s)",?patch_file_path);

/*

File?format:

0???8???"BSDIFF40"

8???8???X

16??8???Y

24??8???sizeof(newfile)

32??X???bzip2(control?block)

32+X????Y???bzip2(diff?block)

32+X+Y??????bzip2(extra?block)

with?control?block?a?set?of?triples?(x,y,z)?meaning?"add?x?bytes

from?oldfile?to?x?bytes?from?the?diff?block;?copy?y?bytes?from?the

extra?block;?seek?forwards?in?oldfile?by?z?bytes".

*/

/*?Read?header?*/

if(fread(header,?1,?32,?f)?

if(feof(f))

errx(1,?"Corrupt?patch\n");

err(1,?"fread(%s)",?patch_file_path);

}

/*?Check?for?appropriate?magic?*/

if(memcmp(header,"BSDIFF40",?8)?!=?0)

errx(1,?"Corrupt?patch\n");

/*?Read?lengths?from?header?*/

bzctrllen=offtin(header+8);

bzdatalen=offtin(header+16);

newsize=offtin(header+24);

if((bzctrllen<0)?||?(bzdatalen<0)?||?(newsize<0))

errx(1,"Corrupt?patch\n");

/*?Close?patch?file?and?re-open?it?via?libbzip2?at?the?right?places?*/

if(fclose(f))

err(1,?"fclose(%s)",?patch_file_path);

if((cpf?=?fopen(patch_file_path,"r"))?==?NULL)

err(1,?"fopen(%s)",?patch_file_path);

if(fseeko(cpf,?32,?SEEK_SET))

err(1,?"fseeko(%s,?%lld)",?patch_file_path,

(longlong)32);

if((cpfbz2?=?BZ2_bzReadOpen(&cbz2err,?cpf,?0,?0,?NULL,?0))?==?NULL)

errx(1,?"BZ2_bzReadOpen,?bz2err?=?%d",?cbz2err);

if((dpf?=?fopen(patch_file_path,"r"))?==?NULL)

err(1,?"fopen(%s)",?patch_file_path);

if(fseeko(dpf,?32?+?bzctrllen,?SEEK_SET))

err(1,?"fseeko(%s,?%lld)",?patch_file_path,

(longlong)(32?+?bzctrllen));

if((dpfbz2?=?BZ2_bzReadOpen(&dbz2err,?dpf,?0,?0,?NULL,?0))?==?NULL)

errx(1,?"BZ2_bzReadOpen,?bz2err?=?%d",?dbz2err);

if((epf?=?fopen(patch_file_path,"r"))?==?NULL)

err(1,?"fopen(%s)",?patch_file_path);

if(fseeko(epf,?32?+?bzctrllen?+?bzdatalen,?SEEK_SET))

err(1,?"fseeko(%s,?%lld)",?patch_file_path,

(longlong)(32?+?bzctrllen?+?bzdatalen));

if((epfbz2?=?BZ2_bzReadOpen(&ebz2err,?epf,?0,?0,?NULL,?0))?==?NULL)

errx(1,?"BZ2_bzReadOpen,?bz2err?=?%d",?ebz2err);

if(((fd=open(old_apk_path,O_RDONLY,0))<0)?||

((oldsize=lseek(fd,0,SEEK_END))==-1)?||

((old=malloc(oldsize+1))==NULL)?||

(lseek(fd,0,SEEK_SET)!=0)?||

(read(fd,old,oldsize)!=oldsize)?||

(close(fd)==-1))?err(1,"%s",old_apk_path);

if((new=malloc(newsize+1))==NULL)?err(1,NULL);

oldpos=0;newpos=0;

while(newpos

/*?Read?control?data?*/

for(i=0;i<=2;i++)?{

lenread?=?BZ2_bzRead(&cbz2err,?cpfbz2,?buf,?8);

if((lenread?

(cbz2err?!=?BZ_STREAM_END)))

errx(1,?"Corrupt?patch\n");

ctrl[i]=offtin(buf);

};

/*?Sanity-check?*/

if(newpos+ctrl[0]>newsize)

errx(1,"Corrupt?patch\n");

/*?Read?diff?string?*/

lenread?=?BZ2_bzRead(&dbz2err,?dpfbz2,?new+?newpos,?ctrl[0]);

if((lenread?

((dbz2err?!=?BZ_OK)?&&?(dbz2err?!=?BZ_STREAM_END)))

errx(1,?"Corrupt?patch\n");

/*?Add?old?data?to?diff?string?*/

for(i=0;i

if((oldpos+i>=0)?&&?(oldpos+i

new[newpos+i]+=old[oldpos+i];

/*?Adjust?pointers?*/

newpos+=ctrl[0];

oldpos+=ctrl[0];

/*?Sanity-check?*/

if(newpos+ctrl[1]>newsize)

errx(1,"Corrupt?patch\n");

/*?Read?extra?string?*/

lenread?=?BZ2_bzRead(&ebz2err,?epfbz2,?new+?newpos,?ctrl[1]);

if((lenread?

((ebz2err?!=?BZ_OK)?&&?(ebz2err?!=?BZ_STREAM_END)))

errx(1,?"Corrupt?patch\n");

/*?Adjust?pointers?*/

newpos+=ctrl[1];

oldpos+=ctrl[2];

};

/*?Clean?up?the?bzip2?reads?*/

BZ2_bzReadClose(&cbz2err,?cpfbz2);

BZ2_bzReadClose(&dbz2err,?dpfbz2);

BZ2_bzReadClose(&ebz2err,?epfbz2);

if(fclose(cpf)?||?fclose(dpf)?||?fclose(epf))

err(1,?"fclose(%s)",?patch_file_path);

/*?Write?the?new?file?*/

if(((fd=open(new_apk_path,O_CREAT|O_TRUNC|O_WRONLY,0666))<0)?||

(write(fd,new,newsize)!=newsize)?||?(close(fd)==-1))

err(1,"%s",new_apk_path);

free(new);

free(old);

return0;

}

原main函數為:

intmain(intargc,char*?argv[])

{

FILE*?f,?*?cpf,?*?dpf,?*?epf;

BZFILE?*?cpfbz2,?*?dpfbz2,?*?epfbz2;

intcbz2err,?dbz2err,?ebz2err;

intfd;

ssize_t?oldsize,newsize;

ssize_t?bzctrllen,bzdatalen;

u_char?header[32],buf[8];

u_char?*old,?*new;

off_t?oldpos,newpos;

off_t?ctrl[3];

off_t?lenread;

off_t?i;

if(argc!=4)?errx(1,"usage:?%s?oldfile?newfile?patchfile\n",argv[0]);

/*?Open?patch?file?*/

if((f?=?fopen(argv[3],"r"))?==?NULL)

err(1,?"fopen(%s)",?argv[3]);

/*

File?format:

0???8???"BSDIFF40"

8???8???X

16??8???Y

24??8???sizeof(newfile)

32??X???bzip2(control?block)

32+X????Y???bzip2(diff?block)

32+X+Y??????bzip2(extra?block)

with?control?block?a?set?of?triples?(x,y,z)?meaning?"add?x?bytes

from?oldfile?to?x?bytes?from?the?diff?block;?copy?y?bytes?from?the

extra?block;?seek?forwards?in?oldfile?by?z?bytes".

*/

/*?Read?header?*/

if(fread(header,?1,?32,?f)?

if(feof(f))

errx(1,?"Corrupt?patch\n");

err(1,?"fread(%s)",?argv[3]);

}

/*?Check?for?appropriate?magic?*/

if(memcmp(header,"BSDIFF40",?8)?!=?0)

errx(1,?"Corrupt?patch\n");

/*?Read?lengths?from?header?*/

bzctrllen=offtin(header+8);

bzdatalen=offtin(header+16);

newsize=offtin(header+24);

if((bzctrllen<0)?||?(bzdatalen<0)?||?(newsize<0))

errx(1,"Corrupt?patch\n");

/*?Close?patch?file?and?re-open?it?via?libbzip2?at?the?right?places?*/

if(fclose(f))

err(1,?"fclose(%s)",?argv[3]);

if((cpf?=?fopen(argv[3],"r"))?==?NULL)

err(1,?"fopen(%s)",?argv[3]);

if(fseeko(cpf,?32,?SEEK_SET))

err(1,?"fseeko(%s,?%lld)",?argv[3],

(longlong)32);

if((cpfbz2?=?BZ2_bzReadOpen(&cbz2err,?cpf,?0,?0,?NULL,?0))?==?NULL)

errx(1,?"BZ2_bzReadOpen,?bz2err?=?%d",?cbz2err);

if((dpf?=?fopen(argv[3],"r"))?==?NULL)

err(1,?"fopen(%s)",?argv[3]);

if(fseeko(dpf,?32?+?bzctrllen,?SEEK_SET))

err(1,?"fseeko(%s,?%lld)",?argv[3],

(longlong)(32?+?bzctrllen));

if((dpfbz2?=?BZ2_bzReadOpen(&dbz2err,?dpf,?0,?0,?NULL,?0))?==?NULL)

errx(1,?"BZ2_bzReadOpen,?bz2err?=?%d",?dbz2err);

if((epf?=?fopen(argv[3],"r"))?==?NULL)

err(1,?"fopen(%s)",?argv[3]);

if(fseeko(epf,?32?+?bzctrllen?+?bzdatalen,?SEEK_SET))

err(1,?"fseeko(%s,?%lld)",?argv[3],

(longlong)(32?+?bzctrllen?+?bzdatalen));

if((epfbz2?=?BZ2_bzReadOpen(&ebz2err,?epf,?0,?0,?NULL,?0))?==?NULL)

errx(1,?"BZ2_bzReadOpen,?bz2err?=?%d",?ebz2err);

if(((fd=open(argv[1],O_RDONLY,0))<0)?||

((oldsize=lseek(fd,0,SEEK_END))==-1)?||

((old=malloc(oldsize+1))==NULL)?||

(lseek(fd,0,SEEK_SET)!=0)?||

(read(fd,old,oldsize)!=oldsize)?||

(close(fd)==-1))?err(1,"%s",argv[1]);

if((new=malloc(newsize+1))==NULL)?err(1,NULL);

oldpos=0;newpos=0;

while(newpos

/*?Read?control?data?*/

for(i=0;i<=2;i++)?{

lenread?=?BZ2_bzRead(&cbz2err,?cpfbz2,?buf,?8);

if((lenread?

(cbz2err?!=?BZ_STREAM_END)))

errx(1,?"Corrupt?patch\n");

ctrl[i]=offtin(buf);

};

/*?Sanity-check?*/

if(newpos+ctrl[0]>newsize)

errx(1,"Corrupt?patch\n");

/*?Read?diff?string?*/

lenread?=?BZ2_bzRead(&dbz2err,?dpfbz2,?new+?newpos,?ctrl[0]);

if((lenread?

((dbz2err?!=?BZ_OK)?&&?(dbz2err?!=?BZ_STREAM_END)))

errx(1,?"Corrupt?patch\n");

/*?Add?old?data?to?diff?string?*/

for(i=0;i

if((oldpos+i>=0)?&&?(oldpos+i

new[newpos+i]+=old[oldpos+i];

/*?Adjust?pointers?*/

newpos+=ctrl[0];

oldpos+=ctrl[0];

/*?Sanity-check?*/

if(newpos+ctrl[1]>newsize)

errx(1,"Corrupt?patch\n");

/*?Read?extra?string?*/

lenread?=?BZ2_bzRead(&ebz2err,?epfbz2,?new+?newpos,?ctrl[1]);

if((lenread?

((ebz2err?!=?BZ_OK)?&&?(ebz2err?!=?BZ_STREAM_END)))

errx(1,?"Corrupt?patch\n");

/*?Adjust?pointers?*/

newpos+=ctrl[1];

oldpos+=ctrl[2];

};

/*?Clean?up?the?bzip2?reads?*/

BZ2_bzReadClose(&cbz2err,?cpfbz2);

BZ2_bzReadClose(&dbz2err,?dpfbz2);

BZ2_bzReadClose(&ebz2err,?epfbz2);

if(fclose(cpf)?||?fclose(dpf)?||?fclose(epf))

err(1,?"fclose(%s)",?argv[3]);

/*?Write?the?new?file?*/

if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0)?||

(write(fd,new,newsize)!=newsize)?||?(close(fd)==-1))

err(1,"%s",argv[2]);

free(new);

free(old);

return0;

}

對比一下,是不是發現。。。。。。。。。。。。。真的很簡單

基本大功告成,剩下的自己解決

======================要割一下才會健康========================

======================不能割,還要留著用========================

node.js服務器這里偷懶一下,就通過調用編譯好的bsdiff二進制文件來實現差分包的產生

為此做了一個npm的包,地址:?https://www.npmjs.org/package/node-bsdiff-android

先介紹使用方法:

varbsdiffApk?=?require('node-bsdiff-android');

varnewApkFile?='./files/new.apk';

varoldApkFile?='./files/old.apk';

varpatchFile?='./files/es.patch';

varbsdiffFile?='./files/bsdiff';

bsdiffApk(oldApkFile,?newApkFile,?patchFile,?function(err,?data)?{

if(err)?{

console.log("Error");

}?else{

console.log("data:?"+?data);

}

});

node.js源碼在github上https://github.com/arthur-zhang/node-bsdiff-android

varexec,?os,?bsdiffApk;

os?=?require('os');

exec?=?require('child_process').execFile;

bsdiffApk?=?function(oldApkFilePath,?newApkFilePath,?patchFilePath,?cb)?{

returnexec(""+?__dirname?+"/bsdiff",?[oldApkFilePath,?newApkFilePath,?patchFilePath],?{

maxBuffer:?1024?*?1024

},?function(err,?out)?{

if(err)?{

returncb(err);

}

cb(null,?out);

});

};

module.exports?=?bsdiffApk;

下面比較重要的是,安裝時下載bsdiff文件,新建install.js

varhttp?=?require('http');

varfs?=?require('fs');

varos?=?require('os');

varplatform?=null;

if(os.type()?=='Darwin')?{

platform?=?'macosx';

}?elseif(os.type()?=='Linux')?{

platform?=?'linux';

}?else{

thrownewError('Unknown?OS!');

}

functionattemptDownload(attemptsLeft)?{

varurl?="http://download.cvte.cn/downfile.php?action=view&file_id=42254&file_key=Iel8FprG";

varfile?=?fs.createWriteStream("bsdiff");

http.get(url,?function(res)?{

res.on('data',function(data)?{

file.write(data);

}).on('end',function()?{

file.end();

fs.chmodSync('bsdiff','755');

});

});

}

attemptDownload(3);

然后在package.json中添加

"scripts":?{

"install":"node?install.js"

},

然后npm addUser(不知道是什么的這部分可以跳過了)

然后npm publish 發布應用

總結

以上是生活随笔為你收集整理的bsdiff php,Apk差分升级Android客户端和Node.js服务端实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。