前端代码
<html>
<head> </head>
<body>
<input id="fileInput" type="file" />
<button id="uploadBtn">上传</button>
<div id="progressBar"></div>
<script>
async function handleUpload() {
const fileInput = document.getElementById("fileInput");
const progressBar = document.getElementById("progressBar");
const uploadBtn = document.getElementById("uploadBtn");
// Disable upload button to prevent multiple uploads
uploadBtn.disabled = true;
const file = fileInput.files[0];
if (!file) {
alert("请选择要上传的文件");
return;
}
// Determine chunk size
const chunkSize = 1024 * 1024; // 1MB (可根据实际需求调整)
// Determine total number of chunks
const numChunks = Math.ceil(file.size / chunkSize);
// Initialize state
let start = 0;
let end = 0;
let currentChunk = 1;
let progress = 0;
let uploadId = "";
// Loop through chunks and upload them one at a time
while (end < file.size) {
end = Math.min(start + chunkSize, file.size);
// Create new FormData object and append current chunk to it
const formData = new FormData();
formData.append("chunk", file.slice(start, end));
// Append state to FormData object
formData.append("currentChunk", currentChunk);
formData.append("numChunks", numChunks);
formData.append("fileName", file.name);
formData.append("fileSize", file.size);
formData.append("uploadId", uploadId);
// Send POST request to server
const result = await fetch("http://10.0.0.6:44368/api/upload", {
method: "POST",
body: formData,
});
// Handle response from server
const json = await result.json();
progress = (currentChunk / numChunks) * 100;
progressBar.style.width = `${progress}%`;
if (json.uploadId) {
uploadId = json.uploadId;
}
// Update state
currentChunk++;
start = end;
}
// Reset progress bar
progressBar.style.width = "0%";
alert("上传完成!");
// Enable upload button
uploadBtn.disabled = false;
}
const uploadBtn = document.getElementById("uploadBtn");
uploadBtn.addEventListener("click", handleUpload);
</script>
</body>
</html>
服务端代码
[HttpPost("api/upload")]
public async Task<IActionResult> Upload()
{
var uploadDirectory = Path.Combine("UploadDirectory");
try
{
// 从请求中读取表单数据
var form = await Request.ReadFormAsync();
var chunk = form.Files.GetFile("chunk");
var currentChunk = int.Parse(form["currentChunk"]);
var numChunks = int.Parse(form["numChunks"]);
var fileName = form["fileName"];
var fileSize = long.Parse(form["fileSize"]);
var uploadId = form["uploadId"];
// 如果未指定uploadId,则生成一个新的
if (string.IsNullOrEmpty(uploadId))
{
uploadId = Guid.NewGuid().ToString();
}
// 确定块目录和文件路径
var chunkDirectory = Path.Combine(uploadDirectory, uploadId);
var chunkPath = Path.Combine(chunkDirectory, $"{currentChunk}.dat");
// 如果块目录不存在,则创建块目录
Directory.CreateDirectory(chunkDirectory);
// 保存数据块到硬盘
await using var fileStream = new FileStream(chunkPath, FileMode.Create, FileAccess.Write);
await chunk?.CopyToAsync(fileStream)!;
fileStream.Close();
// 如果这是最后一个块 则合并所有的块
if (currentChunk == numChunks)
{
var filePath = Path.Combine(uploadDirectory, fileName);
await using var mergedFileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write);
for (var i = 1; i <= numChunks; i++)
{
var chunkFilePath = Path.Combine(chunkDirectory, $"{i}.dat");
// 切片文件 n IN 1
await using var chunkStream = new FileStream(chunkFilePath, FileMode.Open, FileAccess.Read);
await chunkStream.CopyToAsync(mergedFileStream);
}
mergedFileStream.Close();
// 删除块目录
Directory.Delete(chunkDirectory, true);
}
// 将上传的标识信息返回给客户端
var result = new { uploadId };
return Ok(result);
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}
评论区