Skip to content

Instantly share code, notes, and snippets.

@MarkIanHolland
Last active August 23, 2023 15:53
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MarkIanHolland/ce6e8e03527cbe4b85804bc7bb2ef1df to your computer and use it in GitHub Desktop.
Save MarkIanHolland/ce6e8e03527cbe4b85804bc7bb2ef1df to your computer and use it in GitHub Desktop.
FilePond Uploads with .net core
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; }
}
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);
}
}
}
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());
}
}
@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>
@RameshArjampudi
Copy link

Hi can you upload the solution which is working without Amazon please

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment