编程实现Azure Blob文件夹间文件移动未生效问题
问题根因
代码存在3个直接导致文件移动失败的核心问题:
- 混淆了Blob容器和虚拟目录的概念:Azure Blob Storage没有实体文件夹结构,你提到的
Input/Output/Processed本质是同个容器下Blob名称的路径前缀,属于虚拟目录,不是独立容器。代码中调用GetBlobContainerClient传入文件夹名,实际在尝试访问名称为Input、Processed的不存在的容器,根本没有定位到真实存储的文件。 - 异步复制未等待完成就删除源文件:
StartCopyFromUriAsync仅负责启动复制任务、返回操作句柄,不会等待复制执行完成。代码启动复制后立刻删除源Blob,此时复制任务还在执行中,源文件被删会直接导致复制中断失败。 - 手动拼接的源Blob URI无访问权限:你拼接的
blobUri只有资源路径,没有携带访问凭据,如果存储账户未开启公共匿名访问,服务端执行复制时会因为没有源文件读取权限直接失败。
修复步骤
- 先确认你实际存储文件的Blob容器真实名称(绝大多数场景下三个虚拟目录都会归属同一个业务容器,比如命名为
workspace、file-store这类),不要把虚拟目录名当成容器名传入。 - 调整代码逻辑,使用带凭据的源Blob URI发起复制,等待复制完全完成后再删除源文件。
修复后的参考代码:
private async Task MoveFileAsync(string sourceFolder, string destinationFolder, string fileName, ILogger log) { log.LogInformation("Moving file. Source: {0} | Destination: {1} | FileName: {2}.", sourceFolder, destinationFolder, fileName); try { var storageConnection = Environment.GetEnvironmentVariable("StorageConnection"); // 替换为你实际使用的Blob容器名称 const string actualContainerName = "替换为你的真实容器名"; BlobServiceClient blobServiceClient = new BlobServiceClient(storageConnection); BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(actualContainerName); // 拼接虚拟目录下的完整Blob路径 string sourceBlobPath = $"{sourceFolder.TrimEnd('/')}/{fileName}"; string destBlobPath = $"{destinationFolder.TrimEnd('/')}/{fileName}"; BlobClient sourceBlob = containerClient.GetBlobClient(sourceBlobPath); BlobClient destBlob = containerClient.GetBlobClient(destBlobPath); // 直接使用源Blob自带凭据的URI发起复制,无需手动拼接无权限的地址 CopyFromUriOperation copyOperation = await destBlob.StartCopyFromUriAsync(sourceBlob.Uri); // 等待复制任务100%完成 await copyOperation.WaitForCompletionAsync(); // 复制完成后再删除源文件 await sourceBlob.DeleteAsync(); log.LogInformation("File moved completed, source: {0}, target: {1}", sourceBlobPath, destBlobPath); } catch (Exception ex) { log.LogError("Move file failed. {0} | {1} | {2}", ex.Message, ex.StackTrace, ex.InnerException); throw; // 不要吞异常,让Function感知失败触发重试 } }
补充说明
- 如果你的三个目录确实是三个独立的Blob容器,也必须保留
WaitForCompletionAsync的等待逻辑,同时不要手动拼接无凭据的源URI,直接使用sourceBlob.Uri即可,该URI会自动携带连接字符串对应的访问权限。 - 如果处理的都是小体积文件,也可以直接用「下载源文件到内存->上传到目标路径->删除源文件」的逻辑替代服务端异步复制,代码逻辑更简单,不需要处理复制状态轮询的问题。

内容的提问来源于stack exchange,提问作者Caverman
相关产品推荐
相关产品推荐

