Reputation: 1761
I have a IFormFile which I pass as a parameter to two separate asynchronous functions which convert the IFormFile to a memorystream which one uploads to OneDrive via ms graph and one uploads the other to Box via Box SDK. However, sometimes it works and sometimes it fails at the copyToAsync() line in OneDrive. It seems maybe you can only CopyTo one at a time? Because loading to these cloud services are slower I really want to use Async to make things a bit snappier. My code errors as follows:
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
Implementation (I use a few of the same function but vary slightly):
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;
}
}
Box:
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;
}
}
Upvotes: 0
Views: 57