Update ChunkUploadController.cs

This commit is contained in:
Lance McCarthy 2022-06-30 16:45:15 -04:00 коммит произвёл Tsvetomir Tsonev
Родитель 8d6221185b
Коммит f38ab4017e
1 изменённых файлов: 125 добавлений и 54 удалений

Просмотреть файл

@ -1,59 +1,23 @@
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace aspnetcore_upload.Controllers
{
public class ChunkUploadController : Controller
{
private readonly string _root;
private readonly IWebHostEnvironment _webHhostingEnvironment;
public ChunkUploadController(IWebHostEnvironment webHostingEnvironment)
{
_webHhostingEnvironment = webHostingEnvironment;
}
[DataContract]
public class ChunkMetaData
{
[DataMember(Name = "uploadUid")]
public string UploadUid { get; set; }
[DataMember(Name = "fileName")]
public string FileName { get; set; }
[DataMember(Name = "contentType")]
public string ContentType { get; set; }
[DataMember(Name = "chunkIndex")]
public long ChunkIndex { get; set; }
[DataMember(Name = "totalChunks")]
public long TotalChunks { get; set; }
[DataMember(Name = "totalFileSize")]
public long TotalFileSize { get; set; }
}
public void AppendToFile(string fullPath, Stream content)
{
try
{
using (FileStream stream = new FileStream(fullPath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
{
using (content)
{
content.CopyTo(stream);
}
}
}
catch (IOException ex)
{
throw ex;
}
_root = Path.Combine(_webHhostingEnvironment.WebRootPath, "Upload_Directory");
}
public ActionResult Save(List<IFormFile> files)
@ -63,47 +27,77 @@ namespace aspnetcore_upload.Controllers
foreach (var file in files)
{
// Some browsers send file names with full path. This needs to be stripped.
//var fileName = Path.GetFileName(file.FileName);
//var physicalPath = Path.Combine(_webHhostingEnvironment.WebRootPath, "Upload_Directory", fileName);
// The files are not actually saved in this demo
// file.SaveAs(physicalPath);
var fileName = Path.GetFileName(file.FileName);
var physicalPath = Path.Combine(_root, fileName);
using(var uploadedFile = file.OpenReadStream())
using (var stream = new FileStream(physicalPath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
{
uploadedFile.CopyTo(stream);
}
}
}
// Return an empty string to signify success
return Content("");
}
[Route("api/Chunk/Upload")]
[HttpPost]
public ActionResult ChunkSave(List<IFormFile> files, string metaData)
{
// This is not a chunk upload, pass it off to the normal Save method
if (metaData == null)
{
return Save(files);
}
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(metaData));
var ms = new MemoryStream(Encoding.UTF8.GetBytes(metaData));
var serializer = new DataContractJsonSerializer(typeof(ChunkMetaData));
ChunkMetaData somemetaData = serializer.ReadObject(ms) as ChunkMetaData;
string path = String.Empty;
var somemetaData = serializer.ReadObject(ms) as ChunkMetaData;
// The Name of the Upload component is "files"
if (files != null)
{
foreach (var file in files)
{
//path = Path.Combine(_webHhostingEnvironment.WebRootPath, "Upload_Directory", somemetaData.FileName);
// ********************************** //
// ********** Scenario 1 ************ //
// ********************************** //
// If the upload is in sequential order, you can just appends the chunks to what has already been written to disk
//var path = Path.Combine(_root, somemetaData.FileName);
//using (var uploadedChunkStream = file.OpenReadStream())
//using(var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
//{
// uploadedChunkStream.CopyTo(stream);
//}
//AppendToFile(path, file.InputStream);
// ********************************** //
// ********** Scenario 2 ************ //
// ********************************** //
// Un-sequential means you will need to develop a system to keep track of each chunks and assembly them
// together when all the chunks have been uploaded.
// The ChunkMetaData gives you all the information you need to keep track and reassemble
// step 1. Save the chunk
var tempChunkFilePath = Path.Combine(_webHhostingEnvironment.WebRootPath, "Upload_Directory", $"{somemetaData.UploadUid}_{somemetaData.ChunkIndex}");
using (var uploadedChunkStream = file.OpenReadStream())
using (var chunkFileStream = System.IO.File.OpenWrite(tempChunkFilePath))
{
uploadedChunkStream.CopyTo(chunkFileStream);
}
// Step 2. Try to assemble the final file
// In this highly unoptimized implementation, only the lats attempt will have the finished file
TryAssembleFile(somemetaData.UploadUid, somemetaData.TotalChunks, somemetaData.FileName);
}
}
// Return an empty string to signify success
return Content("");
}
[Route("api/Chunk/Remove")]
[HttpPost]
public ActionResult Async_Remove(string[] fileNames)
@ -127,5 +121,82 @@ namespace aspnetcore_upload.Controllers
// Return an empty string to signify success
return Content("");
}
private void TryAssembleFile(string uuid, long totalChunks, string filename)
{
// PHASE 1 - VERIFYING
for (var chunkIndex = 0; chunkIndex <= totalChunks - 1; chunkIndex++)
{
var chunkFilePath = Path.Combine(_webHhostingEnvironment.WebRootPath, "Upload_Directory", $"{uuid}_{chunkIndex}");
// If any of these chunk files are missing, then we know we're not done, break and exit.
if (!System.IO.File.Exists(chunkFilePath))
{
// Exit the method entirely
return;
}
}
// PHASE 2 - COMBINING
// Create a single file to combine everything
var tempFinalFilePath = Path.Combine(_webHhostingEnvironment.WebRootPath, "Upload_Directory", $"{uuid}");
// Open a file stream
using (var destStream = System.IO.File.Create(tempFinalFilePath))
{
// iterate over each chunk, in the order of the ChunkIndex
for (var chunkIndex = 0; chunkIndex <= totalChunks - 1; chunkIndex++)
{
var chunkFileName = Path.Combine(_webHhostingEnvironment.WebRootPath, "Upload_Directory", $"{uuid}_{chunkIndex}");
// Open the chunk's filestream
using var sourceStream = System.IO.File.OpenRead(chunkFileName);
// Copy the chunk to the end of the main file stream
sourceStream.CopyTo(destStream);
}
}
// PHASE 3 - FINALIZING
// Now that we have a combined file, lets move it to the final destination
// Some browsers send file names with full path. This needs to be stripped.
var cleanFileName = Path.GetFileName(filename);
var filePath = Path.Combine(_root, cleanFileName);
if (System.IO.File.Exists(filePath))
System.IO.File.Delete(filePath);
System.IO.File.Move(tempFinalFilePath, filePath);
// PHASE 4 - CLEANUP
// Delete temp chunk files
for (var chunkIndex = 0; chunkIndex <= totalChunks - 1; chunkIndex++)
{
var chunkFileName = Path.Combine(_webHhostingEnvironment.WebRootPath, "Upload_Directory", $"{uuid}_{chunkIndex}");
if (System.IO.File.Exists(chunkFileName))
System.IO.File.Delete(chunkFileName);
}
}
}
[DataContract]
public class ChunkMetaData
{
[DataMember(Name = "uploadUid")]
public string UploadUid { get; set; }
[DataMember(Name = "fileName")]
public string FileName { get; set; }
[DataMember(Name = "contentType")]
public string ContentType { get; set; }
[DataMember(Name = "chunkIndex")]
public long ChunkIndex { get; set; }
[DataMember(Name = "totalChunks")]
public long TotalChunks { get; set; }
[DataMember(Name = "totalFileSize")]
public long TotalFileSize { get; set; }
}
}