آپلود فایلها توسط برنامههای React به یک سرور ASP.NET Core به همراه نمایش درصد پیشرفت
نویسنده: وحید نصیری
تاریخ: ۱۳۹۸/۱۲/۰۸ ۱۳:۴۰
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
dotnet new react
spa.UseReactDevelopmentServer(npmScript: "start");
spa.UseProxyToSpaDevelopmentServer("http://localhost:3000"); > create-react-app clientapp
> cd clientapp > npm install --save bootstrap axios react-toastify
import "bootstrap/dist/css/bootstrap.css";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css"; render() {
return (
<React.Fragment>
<ToastContainer /> import React, { useState } from "react";
import axios from "axios";
import { toast } from "react-toastify";
export default function UploadFileSimple() {
const [description, setDescription] = useState("");
const [selectedFile1, setSelectedFile1] = useState();
const [selectedFile2, setSelectedFile2] = useState();
return (
<form>
<fieldset className="form-group">
<legend>Support Form</legend>
<div className="form-group row">
<label className="form-control-label" htmlFor="description">
Description
</label>
<input
type="text"
className="form-control"
name="description"
onChange={event => setDescription(event.target.value)}
value={description}
/>
</div>
<div className="form-group row">
<label className="form-control-label" htmlFor="file1">
File 1
</label>
<input
type="file"
className="form-control"
name="file1"
onChange={event => setSelectedFile1(event.target.files[0])}
/>
</div>
<div className="form-group row">
<label className="form-control-label" htmlFor="file2">
File 2
</label>
<input
type="file"
className="form-control"
name="file2"
onChange={event => setSelectedFile2(event.target.files[0])}
/>
</div>
<div className="form-group row">
<button
className="btn btn-primary"
type="submit"
>
Submit
</button>
</div>
</fieldset>
</form>
);
} const [description, setDescription] = useState("");
const [selectedFile1, setSelectedFile1] = useState();
const [selectedFile2, setSelectedFile2] = useState(); <input
type="text"
className="form-control"
name="description"
onChange={event => setDescription(event.target.value)}
value={description}
/> <input
type="file"
className="form-control"
name="file1"
onChange={event => setSelectedFile1(event.target.files[0])}
/> <form enctype="multipart/form-data" action="/upload" method="post"> <input id="file-input" type="file" /> </form>
let file = document.getElementById("file-input").files[0];
let formData = new FormData();
formData.append("file", file);
fetch('/upload/image', {method: "POST", body: formData}); // ...
export default function UploadFileSimple() {
// ...
const handleSubmit = async event => {
event.preventDefault();
const formData = new FormData();
formData.append("description", description);
formData.append("file1", selectedFile1);
formData.append("file2", selectedFile2);
toast.success("Form has been submitted successfully!");
setDescription("");
};
return (
<form onSubmit={handleSubmit}>
</form>
);
} // ...
export default function UploadFileSimple() {
const apiUrl = "https://localhost:5001/api/SimpleUpload/SaveTicket";
// ...
const [isUploading, setIsUploading] = useState(false);
const handleSubmit = async event => {
event.preventDefault();
const formData = new FormData();
formData.append("description", description);
formData.append("file1", selectedFile1);
formData.append("file2", selectedFile2);
try {
setIsUploading(true);
const { data } = await axios.post(apiUrl, formData, {
headers: {
"Content-Type": "multipart/form-data"
}}
});
toast.success("Form has been submitted successfully!");
console.log("uploadResult", data);
setIsUploading(false);
setDescription("");
} catch (error) {
setIsUploading(false);
toast.error(error);
}
};
return (
// ...
);
} <button
disabled={ isUploading }
className="btn btn-primary"
type="submit"
>
Submit
</button> const isFileValid = selectedFile => {
if (!selectedFile) {
// toast.error("Please select a file.");
return false;
}
const allowedMimeTypes = [
"image/png",
"image/jpeg",
"image/gif",
"image/svg+xml"
];
if (!allowedMimeTypes.includes(selectedFile.type)) {
toast.error(`Invalid file type: ${selectedFile.type}`);
return false;
}
const maxFileSize = 1024 * 500;
const fileSize = selectedFile.size;
if (fileSize > maxFileSize) {
toast.error(
`File size ${(fileSize / 1024).toFixed(
2
)} KB must be less than ${maxFileSize / 1024} KB`
);
return false;
}
return true;
}; const handleSubmit = async event => {
event.preventDefault();
if (!isFileValid(selectedFile1) || !isFileValid(selectedFile2)) {
return;
} <button
disabled={
isUploading ||
!isFileValid(selectedFile1) ||
!isFileValid(selectedFile2)
}
className="btn btn-primary"
type="submit"
>
Submit
</button> const startTime = Date.now();
const { data } = await axios.post(apiUrl, formData, {
headers: {
"Content-Type": "multipart/form-data"
},
onUploadProgress: progressEvent => {
const { loaded, total } = progressEvent;
const timeElapsed = Date.now() - startTime;
const uploadSpeed = loaded / (timeElapsed / 1000);
setUploadProgress({
queueProgress: Math.round((loaded / total) * 100),
uploadTimeRemaining: Math.ceil((total - loaded) / uploadSpeed),
uploadTimeElapsed: Math.ceil(timeElapsed / 1000),
uploadSpeed: (uploadSpeed / 1024).toFixed(2)
});
}
}); const [uploadProgress, setUploadProgress] = useState({
queueProgress: 0,
uploadTimeRemaining: 0,
uploadTimeElapsed: 0,
uploadSpeed: 0
}); const showUploadProgress = () => {
const {
queueProgress,
uploadTimeRemaining,
uploadTimeElapsed,
uploadSpeed
} = uploadProgress;
if (queueProgress <= 0) {
return <></>;
}
return (
<table className="table">
<thead>
<tr>
<th width="15%">Event</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<strong>Elapsed time</strong>
</td>
<td>{uploadTimeElapsed} second(s)</td>
</tr>
<tr>
<td>
<strong>Remaining time</strong>
</td>
<td>{uploadTimeRemaining} second(s)</td>
</tr>
<tr>
<td>
<strong>Upload speed</strong>
</td>
<td>{uploadSpeed} KB/s</td>
</tr>
<tr>
<td>
<strong>Queue progress</strong>
</td>
<td>
<div
className="progress-bar progress-bar-info progress-bar-striped"
role="progressbar"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow={queueProgress}
style={{ width: queueProgress + "%" }}
>
{queueProgress}%
</div>
</td>
</tr>
</tbody>
</table>
);
}; {showUploadProgress()} const formData = new FormData();
formData.append("description", description);
formData.append("file1", selectedFile1);
formData.append("file2", selectedFile2); using Microsoft.AspNetCore.Http;
namespace UploadFilesSample.Models
{
public class Ticket
{
public int Id { set; get; }
public string Description { set; get; }
public IFormFile File1 { set; get; }
public IFormFile File2 { set; get; }
}
} using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using UploadFilesSample.Models;
namespace UploadFilesSample.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class SimpleUploadController : Controller
{
private readonly IWebHostEnvironment _environment;
public SimpleUploadController(IWebHostEnvironment environment)
{
_environment = environment;
}
[HttpPost("[action]")]
public async Task<IActionResult> SaveTicket([FromForm]Ticket ticket)
{
var file1Path = await saveFileAsync(ticket.File1);
var file2Path = await saveFileAsync(ticket.File2);
//TODO: save the ticket ... get id
return Created("", new { id = 1001 });
}
private async Task<string> saveFileAsync(IFormFile file)
{
const string uploadsFolder = "uploads";
var uploadsRootFolder = Path.Combine(_environment.WebRootPath, "uploads");
if (!Directory.Exists(uploadsRootFolder))
{
Directory.CreateDirectory(uploadsRootFolder);
}
//TODO: Do security checks ...!
if (file == null || file.Length == 0)
{
return string.Empty;
}
var filePath = Path.Combine(uploadsRootFolder, file.FileName);
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(fileStream);
}
return $"/{uploadsFolder}/{file.Name}";
}
}
} public async Task<IActionResult> SaveTicket([FromForm]Ticket ticket)
const { data } = await axios.post(apiUrl, formData, {
headers: {
"Content-Type": "multipart/form-data"
}}
}); return $"/{uploadsFolder}/{file.Name}" <img src="https://localhost:5001/uploads/name.png" />
Don’t call Hooks from regular JavaScript functions. Instead, you can:
✅ Call Hooks from React function components.
✅ Call Hooks from custom Hooks (we’ll learn about them on the next page).