我有一个 IFormFile,我将其作为参数传递给两个单独的异步函数,这两个函数将 IFormFile 转换为内存流,其中一个通过 ms graph 上传到 OneDrive,另一个通过 Box SDK 上传到 Box。但是,在 OneDrive 中的 copyToAsync() 行,有时它可以工作,有时会失败。看来您一次只能复制一个?由于加载到这些云服务的速度较慢,我真的想使用异步来使事情变得更快一些。我的代码错误如下:
The inner stream position has changed unexpectedly.
at Microsoft.AspNetCore.Http.ReferenceReadStream.VerifyPosition()
at Microsoft.AspNetCore.Http.ReferenceReadStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
at System.IO.Stream.<CopyToAsync>g__Core|27_0(Stream source, Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Http.FormFile.CopyToAsync(Stream target, CancellationToken cancellationToken)
at ARMS_API.Repositories.OneDriveRepository.UploadFileByPathAsync(IFormFile file, String DestinationPath, String RootFolderId) in /Users/baun/Documents/ARMS-API/ARMS-API/Repositories/OneDriveCloudStorage.cs:line 45
at ARMS_API.Services.CertificationReviewService`1.AddCertificationAsync(IFormFile file, AddDocumentPOCO addDocumentPOCO) in /Users/baun/Documents/ARMS-API/ARMS-API/Features/CertificationReview/CertificationReviewService.cs:line 89
at ARMS_API.Services.CertificationReviewService`1.AddCertificationReviewAsync(AddCertificationReviewDTO addCertificationReviewDTO) in /Users/baun/Documents/ARMS-API/ARMS-API/Features/CertificationReview/CertificationReviewService.cs:line 74
at ARMS_API.Controllers.CertificationReviewController.AddCertificationReview(AddCertificationReviewDTO addCertificationReviewDTO) in /Users/baun/Documents/ARMS-API/ARMS-API/Features/CertificationReview/CertificationReviewController.cs:line 46
实现(我使用了一些相同的功能,但略有不同):
public async Task<List<DocumentDTO>> AddSupportingDocumentsAsync(AddDocumentsDTO addDocumentsDTO)
{
if(addDocumentsDTO.Files.Count > 0)
{
var currentUser = await _userService.GetByAccessTokenAsync();
var addDocumentsPOCO = await _documentsRepository.AddSupportingDocumentsAsync(addDocumentsDTO, currentUser);
var documentsDTO = new List<DocumentDTO>();
var tasks = addDocumentsPOCO.Select( async file => {
var document = addDocumentsDTO.Files.Where(item => item.FileName.Equals(file.FILENAME_AT_UPLOAD, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
var oneDriveDocumentTask = _oneDriveRepository.UploadFileByPathAsync(document!,Path.Combine(_armsBaseFolderPath, file.DOCUMENT_PATH!, file.DOCUMENT_FILENAME!),_rootOneDriveFolderId);
var boxDocumentTask = _boxRepository.UploadFileByPathAsync(document!,Path.Combine(file.DMCP_DOCUMENT_PATH!, file.DOCUMENT_FILENAME!),_rootBoxFolderId);
List<Task> cloudStorageTasks = new List<Task>(){oneDriveDocumentTask,boxDocumentTask};
await Task.WhenAll(cloudStorageTasks);
documentsDTO.Add(new DocumentDTO{
MmcidForDisplay = file.MMCIDForDisplay,
Mmcid = file.MMCID,
FilenameAtUpload = file.FILENAME_AT_UPLOAD,
DocGroupTypeDd = file.DOC_GROUP_TYPE_DD,
DocIterationCount = file.DOC_ITERATION_COUNT,
DocTypeDd = file.DOC_TYPE_DD,
WhoUploaded = file.WHO_UPLOADED,
DmcpBoxId = file.DMCP_BOX_ID,
DmcpDocumentPath = file.DMCP_DOCUMENT_PATH,
OactDocumentPath = file.DOCUMENT_PATH,
DocumentFilename = file.DOCUMENT_FILENAME
});
});
await Task.WhenAll(tasks);
return documentsDTO;
}
return null;
}
OneDrive:
public async Task<Models.CloudStorage?> UploadFileByPathAsync(IFormFile file, string DestinationPath, string RootFolderId)
{
using(var memoryStream = new MemoryStream())
{
// Use properties to specify the conflict behavior
// in this case, replace
await file.CopyToAsync(memoryStream);
memoryStream.Position = 0;
var oneDriveUpload = await UploadOneDrive(memoryStream, DestinationPath, RootFolderId);
return oneDriveUpload;
}
}
private async Task<Models.CloudStorage?> UploadOneDrive(MemoryStream SourceFile, string DestinationPath, string RootFolderId)
{
using var memoryStream = SourceFile;
// Use properties to specify the conflict behavior
// in this case, replace
var uploadSessionRequestBody = new CreateUploadSessionPostRequestBody
{
Item = new DriveItemUploadableProperties
{
AdditionalData = new Dictionary<string, object>
{
{ "@microsoft.graph.conflictBehavior", "replace" },
},
},
};
// Create the upload session
// itemPath does not need to be a path to an existing item
var uploadSession = await _graphServiceClient!.Drives[RootFolderId].Root
.ItemWithPath(DestinationPath)
.CreateUploadSession
.PostAsync(uploadSessionRequestBody);
// Max slice size must be a multiple of 320 KiB
int maxSliceSize = 320 * 1024;
var fileUploadTask =
new LargeFileUploadTask<DriveItem>(uploadSession, memoryStream, maxSliceSize);
var totalLength = memoryStream.Length;
// Create a callback that is invoked after each slice is uploaded
IProgress<long> progress = new Progress<long>(prog => {
Console.WriteLine($"Uploaded {prog} bytes of {totalLength} bytes");
});
// Upload the file
var uploadResult = await fileUploadTask.UploadAsync(progress);
if (uploadResult.UploadSucceeded)
{
var response = uploadResult.ItemResponse;
var cloudStorage = new Models.CloudStorage () {
CloudStorageProvider = "OneDrive",
Id = response.Id!,
FileName = response.Name!,
WebURL = response.WebUrl!,
DownloadLink = null
};
return cloudStorage;
}
else
{
return null;
}
}
盒子:
public async Task<Models.CloudStorage?> UploadFileByPathAsync(IFormFile file, string DestinationPath, string RootFolderId)
{
using(var memoryStream = new MemoryStream())
{
await file.CopyToAsync(memoryStream);
memoryStream.Position = 0;
var boxUpload = await BoxUpload(memoryStream, DestinationPath, RootFolderId);
return boxUpload;
}
}
private async Task<Models.CloudStorage?> BoxUpload(MemoryStream SourceFile, string DestinationPath, string RootFolderId)
{
BoxClient adminClient = _session.AdminClient(await token.FetchTokenAsync());
string[] directories = Path.GetDirectoryName(DestinationPath)!.Split(Path.DirectorySeparatorChar);
string PrevParentFolderID = RootFolderId;
//finding the folder
try
{
foreach (var folderItem in directories)
{
var limit = 1000;
var currentFolder = await adminClient.FoldersManager.GetFolderItemsAsync(id: PrevParentFolderID,1);
var totalItems = currentFolder.TotalCount;
for (int offset = 0; offset < totalItems; offset+=limit)
{
var folderItems = await adminClient.FoldersManager.GetFolderItemsAsync(id: PrevParentFolderID, limit, offset);
if(folderItems.Entries.Any(x => x.Name.Equals(folderItem, StringComparison.OrdinalIgnoreCase)))
{
PrevParentFolderID = folderItems.Entries.Find(x => x.Name.Equals(folderItem, StringComparison.OrdinalIgnoreCase))!.Id;
break;
}
else
{
if(offset+limit>=totalItems)
{
try
{
// Create a new folder in the user's root folder
var folderParams = new BoxFolderRequest()
{
Name = folderItem.ToString(),
Parent = new BoxRequestEntity() { Id = PrevParentFolderID }
};
BoxFolder folder = await adminClient.FoldersManager.CreateAsync(folderParams);
PrevParentFolderID = folder.Id;
break;
}
catch(BoxException ex)
{
var ExJson = JsonSerializer.Deserialize<dynamic>(ex.Message)!;
PrevParentFolderID = ExJson["context_info"]["conflicts"][0]["id"];
break;
}
}
}
}
}
}
catch (BoxException ex)
{
dynamic ExJson = JsonSerializer.Deserialize<dynamic>(ex.Message)!;
return null;
}
//file upload once folder id is found
try
{
BoxFile file;
using (var fileStream = SourceFile)
{
var fileSizeInMB = fileStream.Length / (1024 * 1024);
if (fileSizeInMB <= 50 )
{
BoxFileRequest requestParams = new BoxFileRequest()
{
Name = Path.GetFileName(DestinationPath),
Parent = new BoxRequestEntity() { Id = PrevParentFolderID }
};
file = await adminClient.FilesManager.UploadAsync(requestParams, fileStream);
}
else
{
var progress = new Progress<BoxProgress>(val => {
Console.WriteLine("Uploaded {0}%", val.progress);
});
file = await adminClient.FilesManager.UploadUsingSessionAsync(fileStream, Path.GetFileName(DestinationPath), PrevParentFolderID, null, progress);
}
}
var cloudStorage = new Models.CloudStorage () {
CloudStorageProvider = "Box",
Id = file.Id,
FileName = file.Name,
WebURL = _productionDomain + "/file/" + file.Id,
DownloadLink = null
};
return cloudStorage;
}
catch (BoxException ex)
{
dynamic ExJson = JsonSerializer.Deserialize<dynamic>(ex.Message)!;
return null;
}
}
当您使用流时,有时光标(又名位置)会移动到流的末尾,您必须将位置移回 0,您尝试过吗?