Azure Blob已提交2个块,但获取Blob仅返回第一个块的问题排查
我使用Azure REST API执行Put Block和Put Block List操作,操作均显示成功完成。通过GET请求访问https://accountname.blob.core.windows.net/container/myblobname?comp=blocklist查询块列表,返回结果如下:
<?xml version="1.0" encoding="utf-8"?> <BlockList> <CommittedBlocks> <Block> <Name>MjAyMy0wNy0xM1QxNTo0Mjo0MS4xM</Name> <Size>1048057</Size> </Block> <Block> <Name>MjAyMy0wNy0xM1QxNTo0MzoxMC44M</Name> <Size>460893</Size> </Block> </CommittedBlocks> </BlockList>
这两个块对应一个视频文件,但直接访问https://accountname.blob.core.windows.net/container/myblobname获取完整视频时,仅返回第一个块,第二个块缺失。以下是我的代码:
export const uploadLongFile = async (param: any) => { const {path, filename, mime} = param; const blockSize = 1 * 1024 * 1024; // 1MB const fileSize = param?.size; const blockCount = Math.ceil(fileSize / blockSize); const sasContainerUri = Config.CONTAINER_URI; const customBlobName = Math.random().toString(16).slice(2); const fileExtension = (filename ?? path).split('.').pop(); const newFileName = `${customBlobName}.${fileExtension}`; const container = 'images'; const assetPath = `${sasContainerUri}/${container}/${newFileName}`; const sasToken = Config.SAS_TOKEN; // you may need to play with other html verbs in this string e.g., `sp`, `ss` e.t.c. const blockIds: any[] = []; const videoChunks: any[] = []; const timeInterval = param?.duration / 1000 / blockCount; const chunks = await TrimWithIntervalVideo(path, timeInterval);// Trim video into blockCount chunks let totalSize = 0; for (let i = 0; i < blockCount; i++) { let dataString = encodeURIComponent(encode(new Date().toISOString())); blockIds.push(dataString); let info = await ReactNativeBlobUtil.fs.stat(videoChunks[i]); let chunkPath = i < 10 ? `${chunks?.url}00${i}.${chunks?.ext}` : i < 100 ? `${chunks?.url}0${i}.${chunks?.ext}` : `${chunks?.url}${i}.${chunks?.ext}`; videoChunks.push(chunkPath); totalSize += info.size; await stageBlock( blockIds[i], videoChunks[i], `${assetPath}?${sasToken}`, info?.size, ); if (i === blockCount - 1) { commitBlockList(blockIds, totalSize, `${assetPath}?${sasToken}`); } } }; const stageBlock = async ( blockId?: any, chunk?: any, url?: any, length?: any, ) => { const res = await ReactNativeBlobUtil.fetch( 'PUT', `${url}&comp=block&blockid=${blockId}`, { 'x-ms-date': new Date().toUTCString(), 'content-length': `${length}`, }, // Change BASE64 encoded data to a file path with prefix `ReactNativeBlobUtil-file://`. // Or simply wrap the file path with ReactNativeBlobUtil.wrap( ReactNativeBlobUtil.wrap(chunk), ).catch(error => console.log({error})); console.log({res}); }; const commitBlockList = async (blockIds: any, length: any, url: any) => { let blockList: any[] = blockIds?.map( (value: string) => `<Latest>${value}</Latest>`, ); let ff = blockList .toString() .replace('[', '') .replace(']', '') .replace(',', '\n'); console.log({url}, {blockList}, {ff}); const res2 = await ReactNativeBlobUtil.fetch( 'PUT', `${url}&comp=blocklist`, { 'x-ms-date': new Date().toUTCString(), 'content-length': `${length}`, 'content-type': 'text/plain; charset=UTF-8', }, `<?xml version="1.0" encoding="utf-8"?> <BlockList> ${ff} </BlockList>`, ) .progress((re, t) => console.log({re}, {t})) .catch(error => console.log({error})); console.log({res2}); };
问题根源分析
块ID重复导致覆盖
循环中每次用new Date().toISOString()生成块ID,但循环执行速度极快,两次生成的时间戳可能完全一致,导致两个块使用同一个ID。后上传的块会覆盖先上传的,最终实际只有一个有效块。视频块路径与文件信息获取顺序颠倒
循环里先执行ReactNativeBlobUtil.fs.stat(videoChunks[i]),但此时videoChunks[i]还未被赋值(后续才执行videoChunks.push(chunkPath)),这会导致获取的文件大小错误,甚至读取不存在的文件,造成上传内容异常。块列表XML格式拼接错误
处理blockList时,用toString().replace(',', '\n')只能替换第一个逗号,多块场景下剩余逗号会保留,导致XML格式不规范。虽然查询块列表显示两个块,但实际提交的XML可能未被正确解析。提交块列表的Content-Type错误
提交块列表时,content-type设置为text/plain; charset=UTF-8,但Azure要求该接口的Content-Type必须是application/xml,格式不匹配可能导致解析异常。
修复建议
- 生成唯一块ID:给时间戳追加循环索引,确保每个块ID唯一,比如:
let dataString = encodeURIComponent(encode(`${new Date().toISOString()}_${i}`)); - 调整代码执行顺序:先生成视频块路径并加入数组,再获取文件信息:
let chunkPath = i < 10 ? `${chunks?.url}00${i}.${chunks?.ext}` : i < 100 ? `${chunks?.url}0${i}.${chunks?.ext}` : `${chunks?.url}${i}.${chunks?.ext}`; videoChunks.push(chunkPath); let info = await ReactNativeBlobUtil.fs.stat(videoChunks[i]); - 正确拼接XML内容:用
join('\n')直接拼接数组元素,避免替换不彻底:let ff = blockList.join('\n'); - 修正Content-Type:提交块列表时,将请求头的
content-type改为application/xml。
内容的提问来源于stack exchange,提问作者BeginnerProgrammer

