如何让Azure WebJob仅下载Azure文件共享中已完整上传的文件?
我碰到过好几个类似的场景,核心问题就是Azure文件共享不会自动标记文件是否处于上传状态,所以WebJob很容易“抢跑”。给你几个实用的解决思路,结合你的代码调整:
解决方案1:用临时文件名标记上传状态
这是最直观的方案——上传端先把文件以临时后缀(比如.tmp)命名,等100%上传完成后,再调用Azure File Share的重命名接口改成正式文件名。WebJob只处理不带临时后缀的文件,从根源上避开未完成的上传文件。
- 上传端操作:上传大文件时先保存为
xxx.ext.tmp,确认上传成功后执行重命名(比如RenameAsync方法)改成xxx.ext - WebJob代码调整:修改文件筛选逻辑,只处理正式后缀的文件:
// 只筛选非目录、且不带.tmp后缀的文件 var shareItems = shareDirectory.GetFilesAndDirectories() .Where(item => !item.IsDirectory && !item.Name.EndsWith(".tmp", StringComparison.OrdinalIgnoreCase)) .ToList();
解决方案2:验证文件大小是否稳定
正在上传的文件,其大小会持续变化。我们可以在检测到文件后,先记录当前大小,等待一小段时间(比如10秒)再检查一次,如果两次大小完全一致,说明上传已经完成,再进行下载操作。
调整后的完整代码示例:
var shareItems = shareDirectory.GetFilesAndDirectories().Where(item => !item.IsDirectory).ToList(); foreach (var item in shareItems) { ShareFileClient file = shareDirectory.GetFileClient(item.Name); if (await file.ExistsAsync()) { // 获取初始文件大小 var initialProps = await file.GetPropertiesAsync(); long initialSize = initialProps.Value.ContentLength; // 等待10秒后再次校验大小 await Task.Delay(TimeSpan.FromSeconds(10)); var updatedProps = await file.GetPropertiesAsync(); long updatedSize = updatedProps.Value.ContentLength; // 两次大小一致,判定为上传完成 if (initialSize == updatedSize) { string filePath = Path.Combine(destPath, item.Name); using (FileStream stream = File.OpenWrite(filePath)) { try { ShareFileDownloadInfo download = await file.DownloadAsync(); download.Content.CopyTo(stream); } catch (Exception ex) { throw; } stream.Flush(); stream.Close(); } // 下载完成后再删除原文件 await file.DeleteAsync(); } else { // 文件还在上传,跳过本次处理,交给下一轮定时任务 continue; } } }
注意:如果你的上传场景存在暂停再恢复的情况,可以把等待时间拉长,或者增加2-3次大小校验的逻辑,确保准确性。
解决方案3:利用文件锁实现排他访问
这个方案可靠性更高,但需要上传端配合:上传文件时先获取文件的独占锁,上传完成后再释放锁。WebJob尝试获取锁,只有成功拿到锁(说明没有其他进程在写入文件),才执行下载和删除操作。
代码示例:
var shareItems = shareDirectory.GetFilesAndDirectories().Where(item => !item.IsDirectory).ToList(); foreach (var item in shareItems) { ShareFileClient file = shareDirectory.GetFileClient(item.Name); if (await file.ExistsAsync()) { try { // 尝试获取5秒超时的独占锁 var leaseResponse = await file.AcquireLeaseAsync(TimeSpan.FromSeconds(5)); // 成功获取锁,说明文件未被上传进程占用 string filePath = Path.Combine(destPath, item.Name); using (FileStream stream = File.OpenWrite(filePath)) { ShareFileDownloadInfo download = await file.DownloadAsync(); download.Content.CopyTo(stream); stream.Flush(); stream.Close(); } // 先释放锁再删除文件 await file.ReleaseLeaseAsync(leaseResponse.Value.LeaseId); await file.DeleteAsync(); } catch (RequestFailedException ex) { // 状态码409表示锁被占用,文件正在上传,直接跳过 if (ex.Status == (int)HttpStatusCode.Conflict) { continue; } // 其他异常正常抛出 throw; } } }
内容的提问来源于stack exchange,提问作者baalu
相关产品推荐
相关产品推荐

