NDK 是什么 | FFmpeg 5.0 编译 so 库

news/2024/5/19 19:21:26/文章来源:https://blog.csdn.net/yinshipin007/article/details/128045455

前言

NDK 全称 Native Development Kit,也就是原生开发工具包 ,官网对它有详细的 中文介绍 。可能一说到 NDKJNI ,大家脑子里第一反应就是集成 C/C++ 。其实 JNI 的含义是 Java Native Interface ,这种接口允许 Java 和其他语言进行交互的,包括但不限于 C/C++ 。目前 Rust 也可以通过 JNI 来和 Java 交互,虽然不太成熟。

其实 NDK 更像一个桥梁,来连通 Java 和其他语言,它是一系列工具的集合。既然作为工具, NDK 并非必须在 Android 项目中才能用。本文我们来通过 NDKFFmpeg 5.0 进行编译,生成动态链接库 so

注:本文的 Java 泛指 JVM 语言,不要拿 Kotlin 抬杠,本质太大的区别 。本文测试项目源码地址【TolyFFmpeg】


一、环境准备

想要编译 FFmpeg 应用 Android 中的动态链接库,我们要准备两个东西:一者是 FFmpeg 的源码;二者是 NDK 的工具包。这两者都可以通过简单的下载获得。


1、FFmpeg 源码下载:5.0.1

作为一个开源项目,想得到源码还是非常简单的。可以在官网直接下载源码,也可以通过 git 来下载,或者点击More releases 来选择某个版本进行下载。 ffmpeg.org/download.ht…


源码解压如下,里面的 doc 文件夹有些文档和案例,还是比较有用的。其余的东西暂时对我们来说并没有什么太大的意义,现在我们的目的是通过这个源码通过 NDK 来编译成在 Android 中可以使用的动态链接库 so 文件。

可能会有人疑惑,那就是 so 库嘛,下载别人的用不就完事了吗?原因很简单,自己编译 FFmpeg 可以手动设置需要的功能,如果直接别人编译好的,就没有设置的机会。而且自己编译也能掌握版本,也就是说,自己动手丰衣足食。


2、下载 NDK :r24

可以在如下网站中下载 NDK 的工具包,不过在 macOS 中更推荐用 Android SDK 管理器来下载,如下在 AndroidStudio 中选择 NDK 点击 OK 下载即可。这里下载的是最新版 r24 (24.0.8215888)

developer.android.google.cn/ndk/downloa…


下载过后 你的AndroidSDK/ndk/24.0.8215888 会议相关文件,说明 NDK 环境准备就绪。


二、编译 FFmpeg

编译 FFmpeg ,只要是使用 ndk 中的编译根据,在 $ndkPath/toolchains/llvm/prebuilt/ 下,不同平台的文件名不同,比如 macOS 中是 darwin-x86_64


1.编译脚本

编译脚本参考: 《使用Android Studio开发FFmpeg的正确姿势》

亲测该脚本在 r24 + 5.0.1 是可用的,使用时注意 tag1tag2 处。

#!/bin/bash
# 用于编译android平台的脚本
​
# NDK所在目录
NDK_PATH=/Users/mac/Coder/SDK/AndroidSDK/ndk/24.0.8215888/ # tag1
# macOS 平台编译,其他平台看一下 $NDK_PATH/toolchains/llvm/prebuilt/ 下的文件夹名称
HOST_PLATFORM=darwin-x86_64  #tag1
# minSdkVersion
API=23
​
TOOLCHAINS="$NDK_PATH/toolchains/llvm/prebuilt/$HOST_PLATFORM"
SYSROOT="$NDK_PATH/toolchains/llvm/prebuilt/$HOST_PLATFORM/sysroot"
# 生成 -fpic 与位置无关的代码
CFLAG="-D__ANDROID_API__=$API -Os -fPIC -DANDROID "
LDFLAG="-lc -lm -ldl -llog "
​
# 输出目录
PREFIX=`pwd`/android-build
# 日志输出目录
CONFIG_LOG_PATH=${PREFIX}/log
# 公共配置
COMMON_OPTIONS=
# 交叉配置
CONFIGURATION=
​
build() {APP_ABI=$1echo "======== > Start build $APP_ABI"case ${APP_ABI} inarmeabi-v7a)ARCH="arm"CPU="armv7-a"MARCH="armv7-a"TARGET=armv7a-linux-androideabiCC="$TOOLCHAINS/bin/$TARGET$API-clang"CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"LD="$TOOLCHAINS/bin/$TARGET$API-clang"# 交叉编译工具前缀CROSS_PREFIX="$TOOLCHAINS/bin/arm-linux-androideabi-"EXTRA_CFLAGS="$CFLAG -mfloat-abi=softfp -mfpu=vfp -marm -march=$MARCH "EXTRA_LDFLAGS="$LDFLAG"EXTRA_OPTIONS="--enable-neon --cpu=$CPU ";;arm64-v8a)ARCH="aarch64"TARGET=$ARCH-linux-androidCC="$TOOLCHAINS/bin/$TARGET$API-clang"CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"LD="$TOOLCHAINS/bin/$TARGET$API-clang"CROSS_PREFIX="$TOOLCHAINS/bin/$TARGET-"EXTRA_CFLAGS="$CFLAG"EXTRA_LDFLAGS="$LDFLAG"EXTRA_OPTIONS="";;x86)ARCH="x86"CPU="i686"MARCH="i686"TARGET=i686-linux-androidCC="$TOOLCHAINS/bin/$TARGET$API-clang"CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"LD="$TOOLCHAINS/bin/$TARGET$API-clang"CROSS_PREFIX="$TOOLCHAINS/bin/$TARGET-"#EXTRA_CFLAGS="$CFLAG -march=$MARCH -mtune=intel -mssse3 -mfpmath=sse -m32"EXTRA_CFLAGS="$CFLAG -march=$MARCH  -mssse3 -mfpmath=sse -m32"EXTRA_LDFLAGS="$LDFLAG"EXTRA_OPTIONS="--cpu=$CPU ";;x86_64)ARCH="x86_64"CPU="x86-64"MARCH="x86_64"TARGET=$ARCH-linux-androidCC="$TOOLCHAINS/bin/$TARGET$API-clang"CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"LD="$TOOLCHAINS/bin/$TARGET$API-clang"CROSS_PREFIX="$TOOLCHAINS/bin/$TARGET-"#EXTRA_CFLAGS="$CFLAG -march=$CPU -mtune=intel -msse4.2 -mpopcnt -m64"EXTRA_CFLAGS="$CFLAG -march=$CPU -msse4.2 -mpopcnt -m64"EXTRA_LDFLAGS="$LDFLAG"EXTRA_OPTIONS="--cpu=$CPU ";;esac
​echo "-------- > Start clean workspace"make clean
​echo "-------- > Start build configuration"CONFIGURATION="$COMMON_OPTIONS"CONFIGURATION="$CONFIGURATION --logfile=$CONFIG_LOG_PATH/config_$APP_ABI.log"CONFIGURATION="$CONFIGURATION --prefix=$PREFIX"CONFIGURATION="$CONFIGURATION --libdir=$PREFIX/libs/$APP_ABI"CONFIGURATION="$CONFIGURATION --incdir=$PREFIX/includes/$APP_ABI"CONFIGURATION="$CONFIGURATION --pkgconfigdir=$PREFIX/pkgconfig/$APP_ABI"CONFIGURATION="$CONFIGURATION --cross-prefix=$CROSS_PREFIX"CONFIGURATION="$CONFIGURATION --arch=$ARCH"CONFIGURATION="$CONFIGURATION --sysroot=$SYSROOT"CONFIGURATION="$CONFIGURATION --cc=$CC"CONFIGURATION="$CONFIGURATION --cxx=$CXX"CONFIGURATION="$CONFIGURATION --ld=$LD"# nm 和 stripCONFIGURATION="$CONFIGURATION --nm=$TOOLCHAINS/bin/llvm-nm"CONFIGURATION="$CONFIGURATION --strip=$TOOLCHAINS/bin/llvm-strip"CONFIGURATION="$CONFIGURATION $EXTRA_OPTIONS"
​echo "-------- > Start config makefile with $CONFIGURATION --extra-cflags=${EXTRA_CFLAGS} --extra-ldflags=${EXTRA_LDFLAGS}"./configure ${CONFIGURATION} \--extra-cflags="$EXTRA_CFLAGS" \--extra-ldflags="$EXTRA_LDFLAGS"
​echo "-------- > Start make $APP_ABI with -j1"make -j1
​echo "-------- > Start install $APP_ABI"make installecho "++++++++ > make and install $APP_ABI complete."
​
}
​
build_all() {#配置开源协议声明COMMON_OPTIONS="$COMMON_OPTIONS --enable-gpl"#目标android平台COMMON_OPTIONS="$COMMON_OPTIONS --target-os=android"#取消默认的静态库COMMON_OPTIONS="$COMMON_OPTIONS --disable-static"COMMON_OPTIONS="$COMMON_OPTIONS --enable-shared"COMMON_OPTIONS="$COMMON_OPTIONS --enable-protocols"#开启交叉编译COMMON_OPTIONS="$COMMON_OPTIONS --enable-cross-compile"COMMON_OPTIONS="$COMMON_OPTIONS --enable-optimizations"COMMON_OPTIONS="$COMMON_OPTIONS --disable-debug"#尽可能小COMMON_OPTIONS="$COMMON_OPTIONS --enable-small"COMMON_OPTIONS="$COMMON_OPTIONS --disable-doc"#不要命令(执行文件)COMMON_OPTIONS="$COMMON_OPTIONS --disable-programs"  # do not build command line programsCOMMON_OPTIONS="$COMMON_OPTIONS --disable-ffmpeg"    # disable ffmpeg buildCOMMON_OPTIONS="$COMMON_OPTIONS --disable-ffplay"    # disable ffplay buildCOMMON_OPTIONS="$COMMON_OPTIONS --disable-ffprobe"   # disable ffprobe buildCOMMON_OPTIONS="$COMMON_OPTIONS --disable-symver"COMMON_OPTIONS="$COMMON_OPTIONS --disable-network"COMMON_OPTIONS="$COMMON_OPTIONS --disable-x86asm"COMMON_OPTIONS="$COMMON_OPTIONS --disable-asm"#启用COMMON_OPTIONS="$COMMON_OPTIONS --enable-pthreads"COMMON_OPTIONS="$COMMON_OPTIONS --enable-mediacodec"COMMON_OPTIONS="$COMMON_OPTIONS --enable-jni"COMMON_OPTIONS="$COMMON_OPTIONS --enable-zlib"COMMON_OPTIONS="$COMMON_OPTIONS --enable-pic"COMMON_OPTIONS="$COMMON_OPTIONS --enable-muxer=flv"#COMMON_OPTIONS="$COMMON_OPTIONS --enable-avresample"COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=h264"COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=mpeg4"COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=mjpeg"COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=png"COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=vorbis"COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=opus"COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=flac"echo "COMMON_OPTIONS=$COMMON_OPTIONS"echo "PREFIX=$PREFIX"echo "CONFIG_LOG_PATH=$CONFIG_LOG_PATH"mkdir -p ${CONFIG_LOG_PATH}build "armeabi-v7a"build "arm64-v8a"build "x86"build "x86_64"
}
​
echo "-------- Start --------"
build_all
echo "-------- End --------"
复制代码

2. 使用脚本

把上面的脚本写在 build_android.sh 中,放在 ffmpeg 源码根目录下。

进入到 ffmpeg 源码目录,执行如下命令,等待即可。

chmod +x build_android.sh
./build_android.sh
复制代码

如下在当前文件夹下会生成 android-build 文件夹,其中 libs 文件夹中盛放着各种架构的 so 库,includes 文件夹中盛放着各种架构的头文件。如果不想编译处某种架构的,在 build_android.sh 的末尾处注释即可。

mac@macdeMacBook-Pro android-build % tree -L 2
.
├── includes
│   ├── arm64-v8a
│   ├── armeabi-v7a
│   ├── x86
│   └── x86_64
├── libs
│   ├── arm64-v8a
│   ├── armeabi-v7a
│   ├── x86
│   └── x86_64
└── share└── ffmpeg
复制代码

从这里可以看出,把 FFmpeg 源码编译成 so 动态链接库,是 NDK 的功劳。其实在 Android 开发中,NDK 的作用也是如此,核心价值也是把其他语言编译成Android 平台可以访问的 so 而已。所以也不要觉得 NDK 有多么神秘,就是一个工具集而已。


三、Android 中集成 FFmpeg

AndroidStudio 中选择创建一个 Native C++ 的项目。其实这也不是必须的,普通项目也可以通过配置来支持 C++


1. app 下的 build.gradle 修改建议

最好在 app/build.gradle 中指定 NDK 的版本,否则可能会下载其他版本的 NDK 而浪费时间。

android {ndkVersion "24.0.8215888"sourceSets {main {jniLibs.srcDirs = ['jniLibs']}}//...  
}
复制代码

另外添加 jniLibs.srcDirs 的指向为了解决下面的异常,而且 jniLibs.srcDirs 指向什么目录都无所谓,但不加引入 so 时就会报错。官网说是 jniLibs 已经默认成为了目录,不需要指定 jniLibs.srcDirs ,但这里感觉莫名其妙,必须要指一下。


2. 项目结构

cpp 文件夹中处理 c++ 相关内容,jniLibs 文件夹放入文件编译的 so 库:


3. CMakeLists.txt 书写

CMakeLists 是构建的脚本,这里先使用 avcodec 打印一下配置信息,不过 ffmpeg 5.0 好像 avcodec 依赖了 swresampleavutil 模块。这里也需要添加一些,记得 4.2.7 的时候还不需要。

cmake_minimum_required(VERSION 3.18.1)
​
project("tolyffmpeg")
​
#引入头文件
include_directories(includes)
# 定义当前 so 库 - 在 java 代码中加载
add_library(tolyffmpeg SHARED native-lib.cpp)
​
# 添加 ffmpeg 的 avcodec、swresample、avutil 模块 start======
set(distribution_DIR ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
​
add_library(avcodec SHARED IMPORTED)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${distribution_DIR}/libavcodec.so)
​
add_library(swresample SHARED IMPORTED)
set_target_properties(swresample PROPERTIES IMPORTED_LOCATION ${distribution_DIR}/libswresample.so)
​
add_library(avutil SHARED IMPORTED)
set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${distribution_DIR}/libavutil.so)
# 添加 ffmpeg 的 avcodec、swresample、avutil 模块 end======
​
find_library(log-lib log)
target_link_libraries(tolyffmpegavcodecswresampleavutil${log-lib})
复制代码

4. 构建产物

点击小锤子,可以在 build 中看到一些构建产物,其中的 so 只会包含引入的相关模块:


默认情况下四种架构都会构建,可以在 app/build.gradle 中指定只构建哪些,比较支持的架构越多,应用体积越大。如下所示:

android {defaultConfig {externalNativeBuild {cmake {abiFilters 'armeabi-v7a', 'arm64-v8a'}}}
复制代码

5. C++ 代码修改和运行结果

如下代码,引入了 libavcodec/avcodec.h 头文件,使用其中的 avcodec_configuration 方法获取信息,进行返回。

---->[src/main/cpp/native-lib.cpp]----
#include <jni.h>
#include <string>
extern "C"{
#include <libavcodec/avcodec.h>
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_toly1994_tolyffmpeg_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {std::string hello = "Hello from C++";return env->NewStringUTF(avcodec_configuration());
}
复制代码

如下就是编译时的配置信息,通过 C++ 获取,返回给 Java 端,进行显示。


四、小结

这就是最基本的利用 NDK 编译 FFmpeg 动态链接库。其实仔细想想,项目中的 C++ 文件也是被 NDK 编译成 libtolyffmpeg.so 库,才能被 Java 所调用。

最后用官网的几句话收尾:Android NDK 是一组使您能将 C 或 C++(“原生代码”)嵌入到 Android 应用中的工具。 NDK 将 C 和 C++ 代码编译到原生库中,然后使用 Android Studio 的集成构建系统 Gradle 将原生库打包到 APK 中。Java 代码随后可以通过 Java 原生接口 (JNI) 框架调用原生库中的函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_225692.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ovs vxlan 时延和吞吐

设计云时到底要不要用vxlan&#xff0c;如果用vxlan到底要不要购买比较贵的smart nic做offload&#xff0c;采用软件vxlan还是硬件交换机vxlan&#xff0c;很难决策&#xff0c;这儿简单测试一下&#xff0c;给个参考&#xff0c;资源终究是有限的&#xff0c;成本还是有考虑的…

【HDU No. 2586】 树上距离 How far away ?

【HDU No. 2586】 树上距离 How far away &#xff1f; 杭电 OJ 题目地址 【题意】 有n 栋房屋&#xff0c;由一些双向道路连接起来。 每两栋房屋之间都有一条独特的简单道路&#xff08;“简单”意味着不可以通过两条道路去一个地方&#xff09;。人们每天总是喜欢这样问&a…

Linux 软链接 与 硬链接 的区别

Linux 软链接 与 硬链接 的区别 1、概念 ​  链接文件&#xff1a;是 Linux 操作系统中的一种文件&#xff0c;主要用于解决文件的共享使用问题&#xff0c;而链接的方式分为两种——软链接和硬链接。 ​  inode&#xff1a;是文件系统中存储文件元信息&#xff08;文件的…

3.71 OrCAD新建原理图时,每一个类目的含义是什么?OrCAD软件怎么显示元器件的封装名称?

笔者电子信息专业硕士毕业&#xff0c;获得过多次电子设计大赛、大学生智能车、数学建模国奖&#xff0c;现就职于南京某半导体芯片公司&#xff0c;从事硬件研发&#xff0c;电路设计研究。对于学电子的小伙伴&#xff0c;深知入门的不易&#xff0c;特开次博客交流分享经验&a…

Word处理控件Aspose.Words功能演示:在 Python 中将 Word 文档转换为 PNG、JPEG 或 BMP

MS Word 文件到图像格式的转换让您可以将文档的页面嵌入到您的 Web 或桌面应用程序中。为了在 Python 应用程序中执行此转换&#xff0c;本文介绍了如何使用 Python 将 Word DOCX或DOC文件转换为PNG、JPEG或BMP图像。此外&#xff0c;您将学习如何使用不同的选项控制 Word 到图…

SpringBoot2.7.4整合Redis

目录 一、添加maven依赖 二、添加配置项 三、新增配置类 四、编辑实体类 五、编写接口 六、编写业务层 1.编写service层 2.编写service实现层 七、测试接口 一、添加maven依赖 <dependency><groupId>org.springframework.boot</groupId><artif…

Python测试框架之Pytest基础入门

Pytest简介 Pytest is a mature full-featured Python testing tool that helps you write better programs.The pytest framework makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries. 通过官方网站介绍…

Flink部署之Yarn

Flink部署之Yarn 一、环境准备 1、Flink 是一个分布式的流处理框架&#xff0c;所以实际应用一般都需要搭建集群环境。 需要准备 3 台 Linux 机器。具体要求如下&#xff1a; 系统环境为 CentOS 7.5 版本。安装 Java 8。安装 Hadoop 集群&#xff0c;Hadoop 建议选择 Hadoop…

【代码随想录】二刷-二叉树

# 二叉树《代码随想录》 二叉树的遍历方式 深度优先遍历: 前序遍历(递归法、迭代法): 中左右中序遍历(递归法、迭代法): 左中右后序遍历(递归法、迭代法): 左右中 广度优先遍历: 层序遍历(迭代法) 二叉树的定义 struct TreeNode{int val;TreeNode* left;TreeNode* right;Tree…

React - Ant Design3.x版本安装使用,并按需引入和自定义主题

React - Ant Design3.x版本安装使用&#xff0c;并按需引入和自定义主题一. 安装使用 antd二&#xff0e;antd 高级配置安装 react-app-rewired&#xff0c;对 create-react-app 的默认配置进行自定义安装 babel-plugin-import &#xff0c;按需加载组件代码和样式自定义主题An…

[毕业设计]机器学习水域检测标注算法

前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,有不少课题是研究生级别难度的,对本科同学来说是充满挑战。为帮助大家顺利通过和节省时间与精力投…

IO模型Netty

一、IO模型 对于一次IO操作&#xff0c;数据会先拷贝到内核空间中&#xff0c;然后再从内核空间拷贝到用户空间中&#xff0c;所以一次read操作&#xff0c;会经历以下两个阶段&#xff0c;基于这两个阶段就产生了五种不同的IO模式。 为了避免用户进程直接操作内核&#xff0c;…

Android8.1 MTK 浏览器下载的apk点击无反应不能安装

最近测试人员发现用原生浏览器下载的apk点击安装时无反应&#xff0c;不能安装。 在/vendor/mediatek/proprietary/packages/apps/Browser/src/com/android/browser/DownloadHandler.java 中&#xff0c;发现下载的apk文件缺少了mime类型&#xff0c;如下图 mimetype null造…

RS编码译码误码率性能matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法描述 纠错编码技术在卫星通信、移动通信及数字存储等领域已获得了广泛的应用。RS码作为其中最重要的码类之一,具有优良的纠随机错误和突发错误的能力,被空间数据系统咨询委员会(CCSDS)作为一种…

计算机毕业设计——基于SpringBoot框架的网上购书系统的设计与实现

文章目录前言一、背景及意义选题背景选题目的二、系统设计主要功能运行环境三、系统实现部分页面截图展示部分代码展示四、源码获取前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 二十一世纪是网络化&#xff0c;信息化的时代&#xff0c;为了满足广大…

植入“人工心脏”助患者重获“心”生

【同期】人工心脏移植患者 刘女士这要是在过去的时候也就放弃了&#xff0c;我再活20年&#xff0c;我还能看着我大孙子成家&#xff0c;这就是我最大的希望。【解说】11月22日&#xff0c;人工心脏移植患者和心脏移植患者在即将康复出院前&#xff0c;互相握手庆贺。据了解&am…

18.3 内存池概念、代码实现和详细分析

一&#xff1a;内存池的概念和实现原理概述 malloc&#xff1a;内存浪费&#xff0c;频繁分配小块内存&#xff0c;浪费更加明显。 “内存池”要解决什么问题&#xff1f; 1、减少malloc()的次数&#xff0c;减少malloc()调用次数就意味着减少对内存的浪费 2、减少malloc()的…

Wireshark Ethernet and ARP 实验—Wireshark Lab: Ethernet and ARP v7.0

Wireshark Lab: Ethernet and ARP v7.0 1. Capturing and analyzing Ethernet frames 清除浏览器缓存 使用wireshark抓包并请求网页 修改“捕获数据包列表”窗口&#xff0c;仅显示有关 IP 以下协议的信息。 抓包干扰较多&#xff0c;故分析作者的数据包回答下列问题 包含…

关于WEB端实现电子海图之Openlayers加载切片

记笔记&#xff0c;免忘记&#xff01; 关于WEB端实现电子海图研究之思路 关于WEB端实现电子海图研究二GeoServer GeoServer完成shp文件切矢量图后&#xff0c;我们需要加载GeoServer切片在web上展示。 vector-tiles-tutorial官方示例 以下示例使用openLayers来加载 D:\s…

Django Cookie 与 Session 对比

文章目录原理比较语法比较Cookie 示例创建 Cookie更新 Cookie删除 CookieSession 示例创建 session查询 session删除一组键值对删除 session参考文档本文通过示例演示 Django 中如何创建、查询、删除 Cookie 与 Session。 原理比较 在Web开发中&#xff0c;使用 session 来完成…