obb的生成及使用

2024-09-04 17:58
文章标签 使用 生成 obb

本文主要是介绍obb的生成及使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

obb的制作及使用

obb制作官方文档参考:

  • obb制作及使用官方文档
  • obb文件生成可参考jobb工具生成(官网推荐)

前言:

  Google Play应用商店在上传限制100MB大小,超过该大小的应用必须将超过部分以扩展文件的形式进行上传处理,总共可上传2个扩展文件,每个最大文件可为2GB,同时obb文件格式【扩展文件可以使用任何文件格式(ZIP, PDF, MP4, 等)。不管任何文件格式Android都认为他们是obb(opaque binary blobs)文件】可自选。
  对每个App而言,该目录下最多只能包含2个扩展文件。一个是main扩展文件另外一个是patch扩展文件,所以一般只需要处理main扩展文件。

一 、OBB命名规则

[main|patch].<expansion-version>.<package-name>.obb

文件名包含四部分,其中:

  • main|patch: 用来表示是主扩展还是补充扩展。
  • expansion-version: 当前上传apk的version code。
  • package-name: apk的包名
  • obb:后缀,其实本质就是zip文件,Google play会自动将zip文件改为obb文件。

例如,假设您的APK版本是(versionCode)25,你的包名为com.example.app。如果上传的主扩展文件,该文件被重命名为:

                         main.25.com.example.app.obb

注意:

  • 上传APK时,OBB会另外上传,不能只更新OBB文件
  • 下载应用程序时,OBB文件下载到%external storage%/Android/obb/package name目录

二、obb 生成方法:

1、google官方提供的工具 jobb

  制作成OBB(Opaque Binary Blob)格式文件的一种工具,在Android SDK中, %ANDROID——HOME/tools%中,在tools/bin下有jobb

命令:

jobb -d [目录名称的完整路径] -o [输出目标文件的完整路径] -pn [软件包名] -pv [包版本]

注:如果没有将android sdk配置环境就需要打开命令窗口后将jobb.bat拖入cmd窗口内才能执行

EG:

源文件目录 D:contents\main\assets\ 目标文件夹: D:\obb\output.obb 软件包名 com.example.app 包版本 25
jobb -d D:\contents\ main\assets\ -o D:\obb\output.obb -pn com.example.app -pv 25

注意:

  • jobb -d,-o 需要指定完整路径。如果指定相对路径,%ANDROID_HOME%/tools/将作为相对路径
  • JOBB工具如果不能输出有一定容量的.obb的文件的话,出现错误提示
  • 下表列出了该jobb工具的命令行选项。
选项
描述
-d 设置用于创建OBB文件的输入目录,或在提取(-dump)现有文件时设置输出目录。创建OBB文件时,指定目录及其所有子目录的内容都包含在OBB文件系统中。
-o 指定OBB文件的文件名。创建OBB并提取(转储)其内容时,此参数是必需的。
-pn 指定安装OBB文件的应用程序的软件包名称,该名称对应package于应用程序清单中指定的值。创建OBB文件时需要此参数。
-pv 设置可以挂载OBB文件的应用程序的最低版本,该版本对应android:versionCode于应用程序清单中的值。创建OBB文件时需要此参数。
-k 指定用于加密新OBB文件或解密现有加密OBB文件的密码。
-ov创建OBB文件,该文件是现有OBB文件结构的叠加层。此选项允许将新包装内容装入与先前包装相同的位置,并用于创建以前生成的OBB文件的修补程序版本。覆盖OBB文件中的文件替换具有相同路径的文件。
-dump 提取指定OBB文件的内容。使用此选项时,还必须使用-d 参数指定内容的输出目录。
注意:转储现有OBB文件时,可以省略该 -d 参数以获取文件内的目录列表,而不提取内容。
-v设置该工具的详细输出。
-about显示该jobb工具的版本和帮助信息。
2、使用winRAR进行压缩(推荐)

将需要压缩的文件放入assets 或者根据目录中在使用winRAR进行压缩。
选中assets文件夹右击—添加到压缩文件,打开下面的窗口,根据命名规则进行命名。

tip:
如果你的扩展文件是一些媒体文件并且你不想解压资源包,而是借助media playback call
(例如MediaPlayer.setDataSource() and SoundPool.load())直接播放资源包里面的媒体文件。
那么在创建扩展文件包的时候务必不要压缩文件,而仅仅是打包即可 压缩方式为:存储


这里写图片描述
压缩过程

三、扩展文件的保存位置

  当Android Market下载程序的扩展文件的时候会保存到系统的共享存储区。为了确保程序正常运行,您不能删除、移动或者重命名扩展文件。在某些设备上Market无法自动下载该扩展文件,那么您应该在程序启动的时候去下载该文件并且保存到同样的位置。
扩展文件保存位置如下:

<shared-storage>/Android/obb/<package-name>/
<shared-storage> 代表共享文件的目录路径,通过函数getExternalStorageDirectory()获取;
<package-name> APK的Java包名。其中shared-storage是设备的primary external storage。

  对于每个App而言,该目录下最多只能包含2个扩展文件。一个是main扩展文件另外一个是patch扩展文件。当更新程序的时候,如果有新的扩展文件则新文件会覆盖旧的扩展文件。
  如果您需要解压缩扩展文件来使用,请注意不要删除该.obb文件,并且也不要把文件解压缩到该目录。您应该把解压缩后的文件保存到getExternalFilesDir()返回的目录下面。如果有可能的话,最好使用程序能直接读取的文件格式而不用再次解压缩文件了。Android开发团队提供了一个项目( APK Expansion Zip Library)可以直接读取ZIP文件中的内容而不用解压缩该文件.
  需要注意的是:保存在系统共享存储区的文件,用户和其他APP也可以访问。

四、APK扩展文件使用实例

要在App中使用扩展文件,需要两个附加的Android库项目:

  • Google Market Licensing package-
  • Google Market APK Expansion Library package-

可以通过Android SDK Manager来下载,也可以直接通过如下链接下载:
https://dl-ssl.google.com/android/repository/market_licensing-r02.zip
https://dl-ssl.google.com/android/repository/market_apk_expansion-r01.zip

  下载完成后使用market_licensing-r02.zip文件中的目录google_market_licensing\library来创建一个库项目;
  然后使用market_apk_expansion-r01.zip中的google_market_apk_expansion\downloader_library来创建另外一个库项目。
  同时为了简化对ZIP格式扩展文件的处理,在market_apk_expansion-r01.zip文件中还包含了一个对ZIP文件处理的库项目:google_market_apk_expansion\zip_file。 如果您使用的扩展文件格式是ZIP,那么也可以创建这个库项目。

1. 声明需要的权限
<manifest...><!-- Required to access Android Market Licensing --><uses-permissionandroid:name="com.android.vending.CHECK_LICENSE"/><!-- Required to download files from Android Market --><uses-permissionandroid:name="android.permission.INTERNET"/><!-- Required to keep CPU alive while downloading files (NOT to keep screen awake) --><uses-permissionandroid:name="android.permission.WAKE_LOCK"/><!-- Required to poll the state of the network connection and respond to changes --><uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE"/><!-- Required to check whether Wi-Fi is enabled --><uses-permissionandroid:name="android.permission.ACCESS_WIFI_STATE"/><!-- Required to read and write the expansion files on shared storage --><uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>...
</manifest>

注意:默认情况下,下载库项目需要的API level为4 而APK扩展ZIP库项目需要API level为5.
准备工作完成后,下面来具体看看如何使用扩展文件。

2. 实现下载服务(Implementing the downloader service)

  为了实现在后台下载文件,下载库项目提供了一个Service实现,名称为DownloaderService。您应该继承自这个文件来实现您的下载服务。为了简化下载服务的开发,该DownloaderService还实现了如下功能:

  • 注册一个BroadcastReceiver来监听设备的网络连接状态的改变。如果网络连接断开就暂停下载;如果网络连接恢复就继续下载。-
  • 安排一个 RTC_WAKEUP 通知,当下载服务被终结的时候可以通过该通知来启动下载服务
  • 生成一个通知(Notification )来显示下载的进度以及下载错误等状态
  • 允许您的程序手工的暂停和恢复下载
  • 检测共享存储区挂载了并且可用,在下载文件之前检测 文件是否已经存在、存储空间是否足够。如果出现问题就通知用户。

      您仅仅需要创建一个继承自DownloaderService的类,并且实现如下三个函数即可:
    getPublicKey():您Market账号的 Base64 编码 RSA 公共密钥,可以通过如下网址获取:
    https://market.android.com/publish/Home#ProfileEditorPlace:

getSALT(): 许可策略用来生成混淆器(Obfuscator)的一组随机bytes。

getAlarmReceiverClassName(): 返回您程序中用来重启下载进程的BroadcastReceiver类名称。当某些情况下,下载服务被意外终止的时候通过该BroadcastReceiver类来重新下载。比如 进程管理的程序终止了下载服务。
  

  • 下面是一个DownloaderService类的实现代码:
public class SampleDownloaderService extends DownloaderService {// You must use the public key belonging to your publisher accountpublic static final String BASE64_PUBLIC_KEY ="YourAndroidMarketLVLKey";// You should also modify this saltpublic static final byte[] SALT =new byte[] {1,42, -12, -1,54,98,-100, -12,43,2, -8, -4,9,5, -106, -107, -33,45, -1,84};@Overridepublic String getPublicKey() {return BASE64_PUBLIC_KEY;}@Overridepublic byte[] getSALT() {return SALT;}@Overridepublic String getAlarmReceiverClassName() {return SampleAlarmReceiver.class.getName();}
}

  然后 在Manifest文件中声明该Service即可。非常简单吧!

<application ...><service android:name=".SampleDownloaderService" />    
</application>
  • 实现AlarmReceiver
      为了检测下载进程和重启下载服务,DownloaderService会安排一个RTC_WAKEUP Alarm来发送一个Intent到程序的 BroadcastReceiver。你必需定义这个 BroadcastReceiver 来调用 Downloader Library提供的函数,通过该函数来检测下载状态和在必要的情况下重启下载服务。
      实现这个类也是非常简单的,一般来说只要覆写onReceive()函数并且调用DownloaderClientMarshaller.startDownloadServiceIfRequired()函数即可。
    定义AlarmReceiver继承自BroadcastReceiver如下所示:
public class AlarmReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {try {PrintLog.d("AlarmReceiver startDownloadServiceIfRequired");DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent,YRDownloaderService.class);} catch (NameNotFoundException e) {e.printStackTrace();}       }}

  然后 在Manifest文件中声明该Receiver即可。非常简单吧!

<application ...><service android:name=".AlarmReceiver" />    
</application>
3.开始下载扩展文件

程序的主Activity(通过Launcher图标启动的Activity)应该负责检查扩展文件是否存在、如果不存在就启动下载服务。
使用Downloader Library来下载需要遵守如下步骤:

1)检查文件是否已经下载了
Downloader Library中的Helper类中包含了一些函数来简化这个步骤:
getExtendedAPKFileName(Context, c, boolean mainFile, int versionCode)
doesFileExist(Context c, String fileName, long fileSize)
例如在示例项目中,在Activity的onCreate()函数中通过如下函数来检查文件是否存在:

boolean expansionFilesDelivered() {for(XAPKFile xf : xAPKS) {String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsBase, xf.mFileVersion);if(!Helpers.doesFileExist(this, fileName, xf.mFileSize,false))returnfalse;}returntrue;
}

这里的XAPKFile对象保存了已知扩展文件的版本号和大小以及是否为main扩展文件。如果该函数返回false则启动下载服务。

2)通过 DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, ClassserviceClass)该函数来开始下载。
该函数的参数如下:

  • context: Your application’s Context.
  • notificationClient: 用来启动主Activity的PendingIntent。用在DownloaderService 创建的用来显示下载进度的通知中。当用户选择该通知,系统调用该PendingIntent来打开显示下载进度的Activity(一般而言就是启动下载的Activity)。
  • serviceClass: 程序中继承自DownloaderService的类。在必要的情况下会启动该服务来开始下载。

这个函数返回一个整数来表示是否有必要下载文件。有如下几个值:

  • NO_DOWNLOAD_REQUIRED: 表示文件已经存在或者当前正在下载。
  • LVL_CHECK_REQUIRED:表示需要授权验证来获取下载扩展文件的URL。
  • DOWNLOAD_REQUIRED: 表示扩展文件的URL已经获取到了,但是还没开始下载。
  • LVL_CHECK_REQUIRED 和 DOWNLOAD_REQUIRED 在本质上是一样的,一般而言您无需关注这个状态。在您的主Activity中调用 startDownloadServiceIfRequired(),你只需要看看返回值是否为NO_DOWNLOAD_REQUIRED即可。如果返回值不是NO_DOWNLOAD_REQUIRED, Downloader Library 开始启动下载,您应该更新程序界面来显示下载进度;如果返回值是 NO_DOWNLOAD_REQUIRED,表明该文件已经下载好了,您的程序可以正常启动了。

例如:

@Override
public void onCreate(Bundle savedInstanceState) {// Check if expansion files are available before going any furtherif(!expansionFilesDelivered()) {// Build an Intent to start this activity from the NotificationIntent notifierIntent =newIntent(this, MainActivity.getClass());notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |Intent.FLAG_ACTIVITY_CLEAR_TOP);...PendingIntent pendingIntent = PendingIntent.getActivity(this,0,notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);// Start the download service (if required)intstartResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,pendingIntent, SampleDownloaderService.class);// If download has started, initialize this activity to show download progressif(startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {// This is where you do set up to display the download progress (next step)...return;}// If the download wasn't necessary, fall through to start the app}startApp();// Expansion files are available, start the app
}

3)  当 startDownloadServiceIfRequired() 函数的返回值不是NO_DOWNLOAD_REQUIRED的时候,
调用DownloaderClientMarshaller.CreateStub(IDownloaderClient client, ClassdownloaderService)函数来创建一个IStub实例。这个IStub实例提供了Activity和下载服务之前的绑定功能,这样您的Activity就可以收到下载事件了。
  CreateStub()函数需要一个实现了IDownloaderClient接口的类和DownloaderService的实现类作为参数。一般而言只要让Activity实现IDownloaderClient接口即可。
  Android开发团队推荐在Activity的onCreate()函数中创建IStub对象(在startDownloadServiceIfRequired()函数之后创建)。

例如:

// Start the download service (if required)
int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,pendingIntent, SampleDownloaderService.class);
// If download has started, initialize activity to show progress
if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {// Instantiate a member instance of IStubmDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this,SampleDownloaderService.class);// Inflate layout that shows download progresssetContentView(R.layout.downloader_ui);return;
}

  当onCreate()函数返回以后,Activity会执行onResume()函数,在该函数中调用IStub的 connect() 函数。同样在onStop()函数中调用IStub的 disconnect()函数。

例如:

@Override
protectedvoidonResume() {if(null!= mDownloaderClientStub) {mDownloaderClientStub.connect(this);}super.onResume();
}@Override
protectedvoidonStop() {if(null!= mDownloaderClientStub) {mDownloaderClientStub.disconnect(this);}super.onStop();
}

调用connect()用来绑定Activity和DownloaderService 。

4. 处理下载进度

  要接收下载进度信息,需要实现IDownloaderClient 接口。该接口有如下函数:

onServiceConnected(Messenger m)
  在初始化完IStub后,会回调该函数。该函数的参数是用来访问您的DownloaderService的,通过 DownloaderServiceMarshaller.CreateProxy()函数来创建这个IDownloaderService对象,然后可以用这个对象来控制下载服务,比如 暂停、继续下载等。

推荐的实现方式:

private IDownloaderService mRemoteService;
...@Override
public void onServiceConnected(Messenger m) {mRemoteService = DownloaderServiceMarshaller.CreateProxy(m);mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger());
}

onDownloadStateChanged(int newState)
  当下载状态发生变化的时候调用该函数,例如 开始下载或者下载完成。

  参数newState的值是IDownloaderClient接口中定义的一些常量之一(以 STATE_ 开头的);
  可以通过函数 Helpers.getDownloaderStringResourceIDFromState()来获取一个状态的文本描述,这样用户更容易理解。例如 STATE_PAUSED_ROAMING 对应的文本描述是: “Download paused because you are roaming/当前在漫游状态,下载停止”

onDownloadProgress(DownloadProgressInfo progress)
  该函数的参数DownloadProgressInfo包含了下载进度的各种信息,例如 预计完成时间、当前下载速度、完成的百分比等。可以根据该信息来更新下载界面。

另外还有一些有用的函数:

  • requestPauseDownload()
    暂停下载
  • requestContinueDownload()
    恢复下载
  • setDownloadFlags(int flags)
    设置下载的网络标示。当前只支持一个标示:FLAGS_DOWNLOAD_OVER_CELLULAR。 通过移动网络下载扩展文件。默认情况下该标示没有启用,所以默认情况下只通过WIFI下载。
5.读取扩展文件

  将APK扩展文件下载下来之后,紧接着我们就要考虑如何使用它了。但是Android 6.0的一些实现(API级别23)以及以后的一些实现仍然需要权限,因此需要在应用程序清单中声明存储权限,并在运行时请求外部存储权限,如下所示:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
或者
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

  在使用obb扩展文件前需要将Zip Library的库移入自己的项目中进行依赖使用此库可以轻松地将ZIP扩展文件中的资源作为虚拟文件系统读取。

Tip:(Zip Library位于其中/extras/google/google_market_apk_expansion/zip_file/)

  使用APKExpansionSupport类来获取obb中的资源
  提供一些访问扩展文件名和ZIP文件的方法:

  • getAPKExpansionFiles()
上面显示的相同方法返回两个扩展文件的完整文件路径。
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
返回ZipResourceFile表示主文件和补丁文件的总和。也就是说,如果同时指定mainVersion和patchVersion,则返回一个ZipResourceFile提供对所有数据的读访问权,并将补丁文件的数据合并到主文件的顶部。
  • ZipResourceFile
表示共享存储上的ZIP文件,并执行基于ZIP文件提供虚拟文件系统的所有工作。您可以使用- APKExpansionSupport.getAPKExpansionZipFile()或ZipResourceFile通过将实例传递给扩展文件来获取实例。此类包含各种有用的方法,但您通常不需要访问其中的大多数方法。一些重要的方法是:
  • getInputStream(String assetPath)
    提供InputStream读取ZIP文件中的文件。的 assetPath必须的路径所需的文件,相对于的ZIP文件内容的根。
  • getAssetFileDescriptor(String assetPath)
    提供一个AssetFileDescriptor对ZIP文件中的文件。的assetPath必须的路径所需的文件,相对于的ZIP文件内容的根。这对某些需要的Android API很有用AssetFileDescriptor

首先要获取扩展文件的路径,可以通过如下代码完成该操作:

// The shared path to all app expansion files
private final static String EXP_PATH ="/Android/obb/";static String[] getAPKExpansionFiles(Context ctx, intmainVersion,intpatchVersion) {String packageName = ctx.getPackageName();Vector<String> ret =newVector<String>();if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {// Build the full path to the app's expansion filesFile root = Environment.getExternalStorageDirectory();File expPath =newFile(root.toString() + EXP_PATH + packageName);// Check that expansion file path existsif(expPath.exists()) {if( mainVersion >0) {String strMainPath = expPath + File.separator +"main."+mainVersion +"."+ packageName +".obb";File main =newFile(strMainPath);if( main.isFile() ) {ret.add(strMainPath);}}if( patchVersion >0) {String strPatchPath = expPath + File.separator +"patch."+mainVersion +"."+ packageName +".obb";File main =newFile(strPatchPath);if( main.isFile() ) {ret.add(strPatchPath);}}}}String[] retArray =newString[ret.size()];ret.toArray(retArray);returnretArray;
}
示例提取资源:
ProvinceUtil    provinceUtil = new ProvinceUtil();
ZipResourceFile expansionFile = null;
try {expansionFile = APKExpansionSupport.getAPKExpansionZipFile(getApplicationContext(), BuildConfig.VERSION_CODE, 0);if (expansionFile != null) {AssetFileDescriptor fd = expansionFile.getAssetFileDescriptor("assets/my_province_data.txt");PrintLog.e("-------" + fd.getLength());InputStream inputStream = expansionFile.getInputStream("assets/my_province_data.txt");if (inputStream != null) {provinceUtil.initProvinceDatas(inputStream);String[] provinces = provinceUtil.getmProvinceDatas();if (provinces != null) {//参考ArrayAdapter的构造函数listView.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,provinces));}}}
} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();
}

这篇关于obb的生成及使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1136629

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

AI一键生成 PPT

AI一键生成 PPT 操作步骤 作为一名打工人,是不是经常需要制作各种PPT来分享我的生活和想法。但是,你们知道,有时候灵感来了,时间却不够用了!😩直到我发现了Kimi AI——一个能够自动生成PPT的神奇助手!🌟 什么是Kimi? 一款月之暗面科技有限公司开发的AI办公工具,帮助用户快速生成高质量的演示文稿。 无论你是职场人士、学生还是教师,Kimi都能够为你的办公文

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];