-
-
Save MarkIanHolland/ce6e8e03527cbe4b85804bc7bb2ef1df to your computer and use it in GitHub Desktop.
FilePond Uploads with .net core
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Attachment | |
{ | |
public int Id { get; set; } | |
public string FileName { get; set; } | |
public string FileType { get; set; } | |
public long FileSize { get; set; } | |
public string Guid { get; set; } | |
public bool Deleted { get; set; } | |
public DateTime CreatedOn { get; set; } | |
public int CaseId { get; set; } | |
public virtual Case Case { get; set; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Amazon.S3; | |
using Amazon.S3.Model; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Mvc; | |
using Microsoft.EntityFrameworkCore; | |
using MinimalFilePond.Data; | |
using MinimalFilePond.Models; | |
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Net.Mime; | |
using System.Text; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace MinimalFilePond.Areas.API | |
{ | |
[Route("api/[controller]")] | |
[ApiController] | |
public class AttachmentController : ControllerBase | |
{ | |
private readonly CaseContext _context; | |
private readonly IAmazonS3 _amazonS3; | |
public AttachmentController(CaseContext context, IAmazonS3 amazonS3) | |
{ | |
_context = context; | |
_amazonS3 = amazonS3; | |
} | |
private static string GetBucketName() => "Bucket"; | |
private Task<bool> UploadToS3Async(MemoryStream newMemoryStream, object guid) | |
{ | |
throw new NotImplementedException(); | |
} | |
private Task<bool> DeleteFromS3Async(DeleteObjectRequest deleteObjectRequest) | |
{ | |
throw new NotImplementedException(); | |
} | |
private Stream GetS3FileStreamAsync(string bucketName, string imageKey) | |
{ | |
throw new NotImplementedException(); | |
} | |
[HttpPost] | |
public async Task<ActionResult> Process([FromForm] int caseId, IFormFile file, CancellationToken cancellationToken) | |
{ | |
if (file is null) | |
{ | |
return BadRequest("Process Error: No file submitted"); | |
} | |
// We do some internal application validation here with our caseId | |
try | |
{ | |
// get a guid to use as the filename as they're highly unique | |
var guid = Guid.NewGuid().ToString(); | |
var newimage = string.Format("{0}.{1}", guid, file.FileName.Split('.').LastOrDefault()); | |
// upload to AWS | |
// We copy the file into a memory stream to pass to S3. | |
//https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-netcore.html | |
using var newMemoryStream = new MemoryStream(); | |
await file.CopyToAsync(newMemoryStream, cancellationToken); | |
var uploadResponse = await UploadToS3Async(newMemoryStream, guid); | |
if (!uploadResponse) | |
{ | |
return BadRequest("Process Error: Upload Failed."); | |
} | |
var attachment = new Attachment | |
{ | |
FileName = file.FileName.Split('.').FirstOrDefault(), | |
FileType = file.FileName.Split('.').LastOrDefault().ToLower(), | |
FileSize = file.Length, | |
CreatedOn = DateTime.Now, | |
CaseId = caseId, | |
Guid = guid | |
}; | |
await _context.AddAsync(attachment, cancellationToken); | |
await _context.SaveChangesAsync(cancellationToken); | |
return Ok(guid); | |
} | |
catch (Exception e) | |
{ | |
return BadRequest($"Process Error: {e.Message}"); // Oops! | |
} | |
} | |
// DELETE: api/RaffleImagesUpload/ | |
// To protect from overposting attacks, enable the specific properties you want to bind to, for | |
// more details, see https://go.microsoft.com/fwlink/?linkid=2123754. | |
[HttpDelete] | |
public async Task<ActionResult> Revert() | |
{ | |
// The server id will be send in the delete request body as plain text | |
using StreamReader reader = new(Request.Body, Encoding.UTF8); | |
string guid = await reader.ReadToEndAsync(); | |
if (string.IsNullOrEmpty(guid)) | |
{ | |
return BadRequest("Revert Error: Invalid unique file ID"); | |
} | |
var attachment = _context.Attachments.FirstOrDefault(i => i.Guid == guid); | |
// We do some internal application validation here | |
try | |
{ | |
// Form the request to delete from s3 | |
var deleteObjectRequest = new DeleteObjectRequest | |
{ | |
BucketName = GetBucketName(), // add your own bucket name | |
Key = guid | |
}; | |
// https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-netcore.html | |
await DeleteFromS3Async(deleteObjectRequest); | |
attachment.Deleted = true; | |
_context.Update(attachment); | |
await _context.SaveChangesAsync(); | |
return Ok(); | |
} | |
catch (AmazonS3Exception e) | |
{ | |
return BadRequest(string.Format("Revert Error:'{0}' when writing an object", e.Message)); | |
} | |
catch (Exception e) | |
{ | |
return BadRequest(string.Format("Revert Error:'{0}' when writing an object", e.Message)); | |
} | |
} | |
[HttpGet("Load/{id}")] | |
public async Task<IActionResult> Load(string id) | |
{ | |
if (string.IsNullOrEmpty(id)) | |
{ | |
return NotFound("Load Error: Invalid parameters"); | |
} | |
var attachment = await _context.Attachments.SingleOrDefaultAsync(i => i.Guid.Equals(id)); | |
if (attachment is null) | |
{ | |
return NotFound("Load Error: File not found"); | |
} | |
var imageKey = string.Format("{0}.{1}", attachment.Guid, attachment.FileType); | |
using Stream ImageStream = GetS3FileStreamAsync(GetBucketName(), imageKey); | |
Response.Headers.Add("Content-Disposition", new ContentDisposition | |
{ | |
FileName = string.Format("{0}.{1}", attachment.FileName, attachment.FileType), | |
Inline = true // false = prompt the user for downloading; true = browser to try to show the file inline | |
}.ToString()); | |
return File(ImageStream, "image/" + attachment.FileType); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class CasesController : Controller | |
{ | |
private readonly CaseContext _context; | |
public CasesController(CaseContext context) | |
{ | |
_context = context; | |
} | |
// GET: Cases | |
public async Task<IActionResult> Index() | |
{ | |
return View(await _context.Case.First()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@model Models.Case | |
<!-- In the document head --> | |
<link href="https://unpkg.com/filepond/dist/filepond.css" rel="stylesheet"> | |
<!-- On your page --> | |
<input type="file" | |
class="filepond" | |
id="filepond" | |
name="files" | |
multiple > | |
<!-- Before the end of the body tag --> | |
<script src="https://unpkg.com/filepond/dist/filepond.js"></script> | |
<script> | |
const ApiUrl = "/api/attachment/"; | |
const inputElement = document.querySelector('input[type="file"]'); | |
function process (fieldName, file, metadata, load, error, progress, abort) { | |
const formData = new FormData(); | |
formData.append(fieldName, file, file.name); | |
formData.append("CaseId", "@Model.Id"); | |
const request = new XMLHttpRequest(); | |
request.open('POST', api); | |
// Setting computable to false switches the loading indicator to infinite mode | |
request.upload.onprogress = (e) => { | |
progress(e.lengthComputable, e.loaded, e.total); | |
}; | |
request.onload = function () { | |
if (request.status >= 200 && request.status < 300) { | |
load(request.responseText);// the load method accepts either a string (id) or an object | |
} | |
else { | |
error('Error during Upload!'); | |
} | |
}; | |
request.send(formData); | |
//expose an abort method so the request can be cancelled | |
return { | |
abort: () => { | |
// This function is entered if the user has tapped the cancel button | |
request.abort(); | |
// Let FilePond know the request has been cancelled | |
abort(); | |
} | |
}; | |
} | |
function remove (source, load, error) { | |
const request = new XMLHttpRequest(); | |
request.open('DELETE', api); | |
// Setting computable to false switches the loading indicator to infinite mode | |
request.upload.onprogress = (e) => { | |
progress(e.lengthComputable, e.loaded, e.total); | |
}; | |
request.onload = function () { | |
if (request.status >= 200 && request.status < 300) { | |
load();// the load method accepts either a string (id) or an object | |
} | |
else { | |
error('Error while removing file!'); | |
} | |
} | |
request.send(source); | |
} | |
const pond = FilePond.create( inputElement, { | |
server: { | |
url: ApiUrl, | |
process: process, | |
load: "/load", | |
fetch: null, | |
remove: remove, | |
}, | |
files: [ | |
@foreach(var attachment in Model.Attachments) | |
{ | |
<text> | |
{ | |
source: "@attachment.Guid", | |
options: { | |
type: 'local', // set type to local to indicate an already uploaded file, so it hits the load endpoint | |
} | |
}, | |
</text> | |
} | |
], | |
}); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi can you upload the solution which is working without Amazon please