步骤二:EdgeOne 边缘函数写入渠道信息到 APK 包
通过 EdgeOne 边缘函数,我们可以动态地将渠道信息写入到 APK 包内。用户只需访问与边缘函数绑定的域名并进行触发配置,就可以触发该边缘函数,从而实现 APK 的动态打包和加速分发。
步骤1:添加用于加速分发的加速域名
说明:
该域名将用于访问下载 APK 安装包。
步骤2:创建用于触发渠道信息写入的边缘函数
1. 根据 函数管理 指引创建一个边缘函数,将如下代码复制到函数代码内。
const CUSTOM_BLOCK_VALUE_LENGTH = 10240;const APK_SIGNING_BLOCK_MAGIC_LENGTH = 16;const APK_SIGNING_BLOCK_OFFSET_LENGTH = 8;const APK_COMMENT_LENGTH = 512;class EdgePack {totalSize;signVersion;centralDirectoryOffset;customBlockValueStart;customBlockValueEnd;rangeRelativeOffset;customInfo;constructor() {this.totalSize = null;this.signVersion = null;this.centralDirectoryOffset = null;this.customBlockValueStart = null;this.customBlockValueEnd = null;this.rangeRelativeOffset = null;this.customInfo = null;}async handle(event) {const { request } = event;/** 1. request 前置校验,不需要处理的 request 直接 fullback */if (!this.checkRequest(request)) {return;}/** 2. fetch 获取源文件 */let response = null;try {response = await fetch(request);} catch (err) {const error = {code: 'FETCH_ORIGIN_ERROR',message: err?.message,};response = new Response(JSON.stringify(error), {status: 590,});}/** 3. response 校验,不需要处理的 response 直接响应客户端 */if (!this.checkResponse(response)) {return event.respondWith(response);}/** 4. 处理 apk 文件,并响应客户端 */const { readable, writable } = new TransformStream();this.handleStream(response, writable);response.headers.set('Cache-Control', 'max-age=0');const streamResponse = new Response(readable, response);event.respondWith(streamResponse);}checkRequest(request) {if (request.method !== 'GET') {return false;}const { pathname, searchParams } = new URL(request.url);/** ATTENTION:默认取 comment 参数,如需修改参数名,请修改此处 */const comment = searchParams?.get('comment');if (!pathname.endsWith('.apk') || !comment) {return false;}this.customInfo = comment;return true;}checkResponse(response) {if (response.status !== 200 && response.status !== 206) {return false;}const contentLength = response.headers.get('Content-Length');if (response.body === null || contentLength === null) {return false;}this.totalSize = Number(contentLength);const cosOffsetHeader = response.headers.get('x-cos-meta-edgepack-offset');const cosTypeHeader = response.headers.get('x-cos-meta-edgepack-type');if (!cosOffsetHeader || !cosTypeHeader) {return false;}this.signVersion = cosTypeHeader;this.centralDirectoryOffset = Number(cosOffsetHeader);if (this.signVersion === 'v1') {this.customBlockValueStart = this.totalSize - APK_COMMENT_LENGTH;this.customBlockValueEnd = this.totalSize;} else {this.customBlockValueStart =this.centralDirectoryOffset -CUSTOM_BLOCK_VALUE_LENGTH -APK_SIGNING_BLOCK_MAGIC_LENGTH -APK_SIGNING_BLOCK_OFFSET_LENGTH;this.customBlockValueEnd = this.centralDirectoryOffset;}this.rangeRelativeOffset = this.getRelativeOffset(response);if (this.rangeRelativeOffset === null) {return false;}return true;}getRelativeOffset(response) {const start = this.customBlockValueStart;const end = this.customBlockValueEnd;const range = response.headers.get('Content-Range');if (!range) return start;const match = range.match(/bytes\s*(\d*)-(\d*)/i);if (!match || match?.length < 2) {return start;}if (+match[2] < start || +match[1] > end) {return null;}return start - +match[1];}async handleStream(response, writable) {const comment = this.customInfo;const relativeOffset = this.rangeRelativeOffset;const responseBody = response.body;const encoder = new TextEncoder();const section = encoder.encode(comment);const writer = writable.getWriter();const reader = responseBody.getReader();try {let handledBytes = 0;while (true) {const result = await reader.read();if (result.done) {console.log('WRITE_COMMENT_DONE');break;}const startByteOffset = handledBytes;const buffer = result.value;handledBytes += buffer.byteLength;const min = Math.max(startByteOffset, relativeOffset);const max = Math.min(relativeOffset + section.byteLength, handledBytes);if (min < max) {const bufferStart = min - startByteOffset;const sectionStart = min - relativeOffset;const sectionEnd = max - relativeOffset;const replacement = section.subarray(sectionStart, sectionEnd);new Uint8Array(buffer).set(replacement, bufferStart);}await writer.ready;await writer.write(buffer);}} catch (err) {console.error('WRITE_COMMENT_ERROR: ', err);}try {await writer.ready;await writer.close();} catch (err) {console.error('CLOSE_WRITER_ERROR: ', err);} finally {writer.releaseLock();}}}async function handleEvent(event) {const edgepack = new EdgePack();await edgepack.handle(event);}addEventListener('fetch', handleEvent);
3. 单击确定,即可完成触发规则的创建。用户访问域名
www.example.com
且文件后缀为.apk
时,即可触发边缘函数进行动态打包。说明: