I'm trying to, on a webpage, select a file and post it in Slack via the Slack API.
I was originally doing:
var request = require('request');
$(".submit").click(function(){
request.post({
url: 'https://slack.com/api/files.upload',
formData: {
token: "myTokenHere",
title: "Image",
filename: "image.png",
filetype: "auto",
channels: "mychannel",
file: document.getElementById('idofuploadelement').files[0]
},
}, function (err, response) {
console.log(JSON.parse(response.body));
});
So then I switched to trying Ajax so that I don't have to include require.
$(".submit").click(function(){
$.ajax({
type: "POST",
method: "POST",
enctype: "multipart/form-data",
url: 'https://slack.com/api/files.upload',
formData: {
token: "myTokenHere",
title: "Image",
filename: "image.png",
filetype: "auto",
channels: "mychannel",
file: document.getElementById('idofuploadelement').files[0]
}
}).done(function(url) {
console.log(url);
});
});
but this gives me the console warning
Form contains a file input, but is missing method=POST and enctype=multipart/form-data on the form. The file will not be sent.
Even though I have those properties (I got the error before adding them too), I still get the error.
My html is simple html5 input tags (for upload and submit button)
<input type="file" name="fileToUpload" id="idofuploadelement">
<input type="submit" name="submit" class="submit action-button next" value="Next"/>
In a nutshell, I'm trying to get a file sent to me (in any way, Slack, Github, email, whatever) without having to spin up a server. Is it possible?
Figured it out:
HTML:
<form id="myform" method="POST" enctype="multipart/form-data">
<input type="file" name="fileToUpload" id="file-select">
<input id="upload-button" type="submit" name="submit" class="submit action-button next" value="Next"/>
</form>
JS:
var form = document.getElementById('myform');
var fileSelect = document.getElementById('file-select');
var uploadButton = document.getElementById('upload-button');
form.onsubmit = function(event) {
event.preventDefault();
// Update button text.
uploadButton.innerHTML = 'Uploading...';
var file = fileSelect.files[0];
var formData = new FormData();
formData.append('file', file, file.name);
formData.append('token', insert_token_here);
formData.append('channels', insert_channel_here);
var xhr = new XMLHttpRequest();
xhr.open('POST','https://slack.com/api/files.upload', true);
// Set up a handler for when the request finishes.
xhr.onload = function () {
if (xhr.status === 200) {
// File(s) uploaded.
uploadButton.innerHTML = 'Uploaded';
} else {
alert('An error occurred!');
}
};
xhr.send(formData);
}
Related
I have a file in my view
<form id="upload" enctype="multipart/form-data">
<input type="file" name="fileUpload" id="fileUpload" size="23" />
</form>
and an ajax request
$.ajax({
url: '<%=Url.Action("JsonSave","Survey") %>',
dataType: 'json',
processData: false,
contentType: "multipart/mixed",
data: {
Id: selectedRow.Id,
Value: 'some date was added by the user here :))'
},
cache: false,
success: function (data) {}
});
but there is no file in the Request.Files. Whats wrong with the ajax request?
Upload files using AJAX in ASP.Net MVC
Things have changed since HTML5
JavaScript
document.getElementById('uploader').onsubmit = function () {
var formdata = new FormData(); //FormData object
var fileInput = document.getElementById('fileInput');
//Iterating through each files selected in fileInput
for (i = 0; i < fileInput.files.length; i++) {
//Appending each file to FormData object
formdata.append(fileInput.files[i].name, fileInput.files[i]);
}
//Creating an XMLHttpRequest and sending
var xhr = new XMLHttpRequest();
xhr.open('POST', '/Home/Upload');
xhr.send(formdata);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
}
return false;
}
Controller
public JsonResult Upload()
{
for (int i = 0; i < Request.Files.Count; i++)
{
HttpPostedFileBase file = Request.Files[i]; //Uploaded file
//Use the following properties to get file's name, size and MIMEType
int fileSize = file.ContentLength;
string fileName = file.FileName;
string mimeType = file.ContentType;
System.IO.Stream fileContent = file.InputStream;
//To save file, use SaveAs method
file.SaveAs(Server.MapPath("~/")+ fileName ); //File will be saved in application root
}
return Json("Uploaded " + Request.Files.Count + " files");
}
EDIT : The HTML
<form id="uploader">
<input id="fileInput" type="file" multiple>
<input type="submit" value="Upload file" />
</form>
AJAX file uploads are now possible by passing a FormData object to the data property of the $.ajax request.
As the OP specifically asked for a jQuery implementation, here you go:
<form id="upload" enctype="multipart/form-data" action="#Url.Action("JsonSave", "Survey")" method="POST">
<input type="file" name="fileUpload" id="fileUpload" size="23" /><br />
<button>Upload!</button>
</form>
$('#upload').submit(function(e) {
e.preventDefault(); // stop the standard form submission
$.ajax({
url: this.action,
type: this.method,
data: new FormData(this),
cache: false,
contentType: false,
processData: false,
success: function (data) {
console.log(data.UploadedFileCount + ' file(s) uploaded successfully');
},
error: function(xhr, error, status) {
console.log(error, status);
}
});
});
public JsonResult Survey()
{
for (int i = 0; i < Request.Files.Count; i++)
{
var file = Request.Files[i];
// save file as required here...
}
return Json(new { UploadedFileCount = Request.Files.Count });
}
More information on FormData at MDN
You can't upload files via ajax, you need to use an iFrame or some other trickery to do a full postback. This is mainly due to security concerns.
Here's a decent write-up including a sample project using SWFUpload and ASP.Net MVC by Steve Sanderson. It's the first thing I read getting this working properly with Asp.Net MVC (I was new to MVC at the time as well), hopefully it's as helpful for you.
I have a sample like this on vuejs version: v2.5.2
<form action="url" method="post" enctype="multipart/form-data">
<div class="col-md-6">
<input type="file" class="image_0" name="FilesFront" ref="FilesFront" />
</div>
<div class="col-md-6">
<input type="file" class="image_1" name="FilesBack" ref="FilesBack" />
</div>
</form>
<script>
Vue.component('v-bl-document', {
template: '#document-item-template',
props: ['doc'],
data: function () {
return {
document: this.doc
};
},
methods: {
submit: function () {
event.preventDefault();
var data = new FormData();
var _doc = this.document;
Object.keys(_doc).forEach(function (key) {
data.append(key, _doc[key]);
});
var _refs = this.$refs;
Object.keys(_refs).forEach(function (key) {
data.append(key, _refs[key].files[0]);
});
debugger;
$.ajax({
type: "POST",
data: data,
url: url,
cache: false,
contentType: false,
processData: false,
success: function (result) {
//do something
},
});
}
}
});
</script>
If you posting form using ajax then you can not
send image using $.ajax method,
you have to use classic xmlHttpobject method for saving image,
other alternative of it use submit type instead of button
I have a file in my view
<form id="upload" enctype="multipart/form-data">
<input type="file" name="fileUpload" id="fileUpload" size="23" />
</form>
and an ajax request
$.ajax({
url: '<%=Url.Action("JsonSave","Survey") %>',
dataType: 'json',
processData: false,
contentType: "multipart/mixed",
data: {
Id: selectedRow.Id,
Value: 'some date was added by the user here :))'
},
cache: false,
success: function (data) {}
});
but there is no file in the Request.Files. Whats wrong with the ajax request?
Upload files using AJAX in ASP.Net MVC
Things have changed since HTML5
JavaScript
document.getElementById('uploader').onsubmit = function () {
var formdata = new FormData(); //FormData object
var fileInput = document.getElementById('fileInput');
//Iterating through each files selected in fileInput
for (i = 0; i < fileInput.files.length; i++) {
//Appending each file to FormData object
formdata.append(fileInput.files[i].name, fileInput.files[i]);
}
//Creating an XMLHttpRequest and sending
var xhr = new XMLHttpRequest();
xhr.open('POST', '/Home/Upload');
xhr.send(formdata);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
}
return false;
}
Controller
public JsonResult Upload()
{
for (int i = 0; i < Request.Files.Count; i++)
{
HttpPostedFileBase file = Request.Files[i]; //Uploaded file
//Use the following properties to get file's name, size and MIMEType
int fileSize = file.ContentLength;
string fileName = file.FileName;
string mimeType = file.ContentType;
System.IO.Stream fileContent = file.InputStream;
//To save file, use SaveAs method
file.SaveAs(Server.MapPath("~/")+ fileName ); //File will be saved in application root
}
return Json("Uploaded " + Request.Files.Count + " files");
}
EDIT : The HTML
<form id="uploader">
<input id="fileInput" type="file" multiple>
<input type="submit" value="Upload file" />
</form>
AJAX file uploads are now possible by passing a FormData object to the data property of the $.ajax request.
As the OP specifically asked for a jQuery implementation, here you go:
<form id="upload" enctype="multipart/form-data" action="#Url.Action("JsonSave", "Survey")" method="POST">
<input type="file" name="fileUpload" id="fileUpload" size="23" /><br />
<button>Upload!</button>
</form>
$('#upload').submit(function(e) {
e.preventDefault(); // stop the standard form submission
$.ajax({
url: this.action,
type: this.method,
data: new FormData(this),
cache: false,
contentType: false,
processData: false,
success: function (data) {
console.log(data.UploadedFileCount + ' file(s) uploaded successfully');
},
error: function(xhr, error, status) {
console.log(error, status);
}
});
});
public JsonResult Survey()
{
for (int i = 0; i < Request.Files.Count; i++)
{
var file = Request.Files[i];
// save file as required here...
}
return Json(new { UploadedFileCount = Request.Files.Count });
}
More information on FormData at MDN
You can't upload files via ajax, you need to use an iFrame or some other trickery to do a full postback. This is mainly due to security concerns.
Here's a decent write-up including a sample project using SWFUpload and ASP.Net MVC by Steve Sanderson. It's the first thing I read getting this working properly with Asp.Net MVC (I was new to MVC at the time as well), hopefully it's as helpful for you.
I have a sample like this on vuejs version: v2.5.2
<form action="url" method="post" enctype="multipart/form-data">
<div class="col-md-6">
<input type="file" class="image_0" name="FilesFront" ref="FilesFront" />
</div>
<div class="col-md-6">
<input type="file" class="image_1" name="FilesBack" ref="FilesBack" />
</div>
</form>
<script>
Vue.component('v-bl-document', {
template: '#document-item-template',
props: ['doc'],
data: function () {
return {
document: this.doc
};
},
methods: {
submit: function () {
event.preventDefault();
var data = new FormData();
var _doc = this.document;
Object.keys(_doc).forEach(function (key) {
data.append(key, _doc[key]);
});
var _refs = this.$refs;
Object.keys(_refs).forEach(function (key) {
data.append(key, _refs[key].files[0]);
});
debugger;
$.ajax({
type: "POST",
data: data,
url: url,
cache: false,
contentType: false,
processData: false,
success: function (result) {
//do something
},
});
}
}
});
</script>
If you posting form using ajax then you can not
send image using $.ajax method,
you have to use classic xmlHttpobject method for saving image,
other alternative of it use submit type instead of button
I'm using jQuery and Ajax for my forms to submit data and files but I'm not sure how to send both data and files in one form?
I currently do almost the same with both methods but the way in which the data is gathered into an array is different, the data uses .serialize(); but the files use = new FormData($(this)[0]);
Is it possible to combine both methods to be able to upload files and data in one form through Ajax?
Data jQuery, Ajax and html
$("form#data").submit(function(){
var formData = $(this).serialize();
$.ajax({
url: window.location.pathname,
type: 'POST',
data: formData,
async: false,
success: function (data) {
alert(data)
},
cache: false,
contentType: false,
processData: false
});
return false;
});
<form id="data" method="post">
<input type="text" name="first" value="Bob" />
<input type="text" name="middle" value="James" />
<input type="text" name="last" value="Smith" />
<button>Submit</button>
</form>
Files jQuery, Ajax and html
$("form#files").submit(function(){
var formData = new FormData($(this)[0]);
$.ajax({
url: window.location.pathname,
type: 'POST',
data: formData,
async: false,
success: function (data) {
alert(data)
},
cache: false,
contentType: false,
processData: false
});
return false;
});
<form id="files" method="post" enctype="multipart/form-data">
<input name="image" type="file" />
<button>Submit</button>
</form>
How can I combine the above so that I can send data and files in one form via Ajax?
My aim is to be able to send all of this form in one post with Ajax, is it possible?
<form id="datafiles" method="post" enctype="multipart/form-data">
<input type="text" name="first" value="Bob" />
<input type="text" name="middle" value="James" />
<input type="text" name="last" value="Smith" />
<input name="image" type="file" />
<button>Submit</button>
</form>
The problem I had was using the wrong jQuery identifier.
You can upload data and files with one form using ajax.
PHP + HTML
<?php
print_r($_POST);
print_r($_FILES);
?>
<form id="data" method="post" enctype="multipart/form-data">
<input type="text" name="first" value="Bob" />
<input type="text" name="middle" value="James" />
<input type="text" name="last" value="Smith" />
<input name="image" type="file" />
<button>Submit</button>
</form>
jQuery + Ajax
$("form#data").submit(function(e) {
e.preventDefault();
var formData = new FormData(this);
$.ajax({
url: window.location.pathname,
type: 'POST',
data: formData,
success: function (data) {
alert(data)
},
cache: false,
contentType: false,
processData: false
});
});
Short Version
$("form#data").submit(function(e) {
e.preventDefault();
var formData = new FormData(this);
$.post($(this).attr("action"), formData, function(data) {
alert(data);
});
});
another option is to use an iframe and set the form's target to it.
you may try this (it uses jQuery):
function ajax_form($form, on_complete)
{
var iframe;
if (!$form.attr('target'))
{
//create a unique iframe for the form
iframe = $("<iframe></iframe>").attr('name', 'ajax_form_' + Math.floor(Math.random() * 999999)).hide().appendTo($('body'));
$form.attr('target', iframe.attr('name'));
}
if (on_complete)
{
iframe = iframe || $('iframe[name="' + $form.attr('target') + '"]');
iframe.load(function ()
{
//get the server response
var response = iframe.contents().find('body').text();
on_complete(response);
});
}
}
it works well with all browsers, you don't need to serialize or prepare the data.
one down side is that you can't monitor the progress.
also, at least for chrome, the request will not appear in the "xhr" tab of the developer tools but under "doc"
I was having this same issue in ASP.Net MVC with HttpPostedFilebase and instead of using form on Submit I needed to use button on click where I needed to do some stuff and then if all OK the submit form so here is how I got it working
$(".submitbtn").on("click", function(e) {
var form = $("#Form");
// you can't pass Jquery form it has to be javascript form object
var formData = new FormData(form[0]);
//if you only need to upload files then
//Grab the File upload control and append each file manually to FormData
//var files = form.find("#fileupload")[0].files;
//$.each(files, function() {
// var file = $(this);
// formData.append(file[0].name, file[0]);
//});
if ($(form).valid()) {
$.ajax({
type: "POST",
url: $(form).prop("action"),
//dataType: 'json', //not sure but works for me without this
data: formData,
contentType: false, //this is requireded please see answers above
processData: false, //this is requireded please see answers above
//cache: false, //not sure but works for me without this
error : ErrorHandler,
success : successHandler
});
}
});
this will than correctly populate your MVC model, please make sure in your Model, The Property for HttpPostedFileBase[] has the same name as the Name of the input control in html i.e.
<input id="fileupload" type="file" name="UploadedFiles" multiple>
public class MyViewModel
{
public HttpPostedFileBase[] UploadedFiles { get; set; }
}
Or shorter:
$("form#data").submit(function() {
var formData = new FormData(this);
$.post($(this).attr("action"), formData, function() {
// success
});
return false;
});
EDIT: with the new version of JQuery (3.6), you could also try using contentType function argument instead of enctype. Try contentType: multipart/form-data.
For me, it didn't work without enctype: 'multipart/form-data' field in the Ajax request. I hope it helps someone who is stuck in a similar problem.
Even though the enctype was already set in the form attribute, for some reason, the Ajax request didn't automatically identify the enctype without explicit declaration (jQuery 3.3.1).
// Tested, this works for me (jQuery 3.3.1)
fileUploadForm.submit(function (e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: $(this).attr('action'),
enctype: 'multipart/form-data',
data: new FormData(this),
processData: false,
contentType: false,
success: function (data) {
console.log('Thank God it worked!');
}
}
);
});
// enctype field was set in the form but Ajax request didn't set it by default.
<form action="process/file-upload" enctype="multipart/form-data" method="post" >
<input type="file" name="input-file" accept="text/plain" required>
...
</form>
As others mentioned above, please also pay special attention to the contentType and processData fields.
A Simple but more effective way:
new FormData() is itself like a container (or a bag). You can put everything attr or file in itself.
The only thing you'll need to append the attribute, file, fileName eg:
let formData = new FormData()
formData.append('input', input.files[0], input.files[0].name)
and just pass it in AJAX request. Eg:
let formData = new FormData()
var d = $('#fileid')[0].files[0]
formData.append('fileid', d);
formData.append('inputname', value);
$.ajax({
url: '/yourroute',
method: 'POST',
contentType: false,
processData: false,
data: formData,
success: function(res){
console.log('successfully')
},
error: function(){
console.log('error')
}
})
You can append n number of files or data with FormData.
and if you're making AJAX Request from Script.js file to Route file in Node.js beware of using
req.body to access data (ie text)
req.files to access file (ie image, video etc)
The code below works for me
$(function () {
debugger;
document.getElementById("FormId").addEventListener("submit", function (e) {
debugger;
if (ValidDateFrom()) { // Check Validation
var form = e.target;
if (form.getAttribute("enctype") === "multipart/form-data") {
debugger;
if (form.dataset.ajax) {
e.preventDefault();
e.stopImmediatePropagation();
var xhr = new XMLHttpRequest();
xhr.open(form.method, form.action);
xhr.onreadystatechange = function (result) {
debugger;
if (xhr.readyState == 4 && xhr.status == 200) {
debugger;
var responseData = JSON.parse(xhr.responseText);
SuccessMethod(responseData); // Redirect to your Success method
}
};
xhr.send(new FormData(form));
}
}
}
}, true);
});
In your Action Post Method, pass parameter as HttpPostedFileBase UploadFile and make sure your file input has same as mentioned in your parameter of the Action Method.
It should work with AJAX Begin form as well.
Remember over here that your AJAX BEGIN Form will not work over here since you make your post call defined in the code mentioned above and you can reference your method in the code as per the Requirement
I know I am answering late but this is what worked for me
Just to remind, in 2022 you don't need to use jquery. Try js standard Fetch API
var formData = new FormData(this);
fetch(url, {
method: 'POST',
body: formData
})
.then(response => {
if(response.ok) {
//success
alert(response);
} else {
throw Error('Server error');
}
})
.catch(error => {
console.log('fail', error);
});
This is a solution that I implemented
var formData = new FormData();
var files = $('input[type=file]');
for (var i = 0; i < files.length; i++) {
if (files[i].value == "" || files[i].value == null) {
return false;
}
else {
formData.append(files[i].name, files[i].files[0]);
}
}
var formSerializeArray = $("#Form").serializeArray();
for (var i = 0; i < formSerializeArray.length; i++) {
formData.append(formSerializeArray[i].name, formSerializeArray[i].value)
}
$.ajax({
type: 'POST',
data: formData,
contentType: false,
processData: false,
cache: false,
url: '/Controller/Action',
success: function (response) {
if (response.Success == true) {
return true;
}
else {
return false;
}
},
error: function () {
return false;
},
failure: function () {
return false;
}
});
---Solution for DOT NET CORE MVC Implementation---
While looking at this question I though I should right .NET CORE implementation for this because the question is not specific to any backend language.
So guys here is the standalone implementation example.
Objective :- To submit form fields including files and how we can get data in a single model at backend
HTML Code / View Code - Views/Home/Index.cshtml
#{
ViewData["Title"] = "Home Page";
}
<input type="file" id="FileUpload1" multiple />
<div>
<label>Enter First Name :</label>
<input type="text" id="nameText" maxlength="50" />
</div>
<input type="button" id="btnUpload" value="Submit Form with Files" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>
$(document).ready(function () {
$('#btnUpload').click(function () {
// Checking whether FormData is available in browser
if (window.FormData !== undefined) {
var fileUpload = $("#FileUpload1").get(0);
var files = fileUpload.files;
// Create FormData object
var fileData = new FormData();
// Looping over all files and add it to FormData object
for (var i = 0; i < files.length; i++) {
fileData.append("files", files[i]);
}
// Adding one more key to FormData object
fileData.append('FirstName', $("#nameText").val());
$.ajax({
url: '/Home/UploadFiles',
type: "POST",
contentType: false, // Not to set any content header
processData: false, // Not to process data
data: fileData,
success: function (result) {
alert(result);
},
error: function (err) {
alert(err.statusText);
}
});
} else {
alert("FormData is not supported.");
}
});
});
</script>
Backend Code / Controller action method Controllers/HomeController.cs
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IWebHostEnvironment _environment;
public HomeController(ILogger<HomeController> logger, IWebHostEnvironment environment)
{
_logger = logger;
_environment = environment;
}
public IActionResult Index()
{
return View();
}
public IActionResult Privacy()
{
return View();
}
[HttpPost]
public async Task<IActionResult> UploadFiles(MyForm myForm)
{
var files = myForm.Files;
// First Name
string name = myForm.FirstName;
// check All files
foreach (IFormFile source in files)
{
string filename = ContentDispositionHeaderValue.Parse(source.ContentDisposition).FileName.Trim('"');
filename = this.EnsureCorrectFilename(filename);
string fileWithPath = this.GetPathAndFilename(filename);
// Create directory if not exist
Directory.CreateDirectory(Path.GetDirectoryName(fileWithPath));
using (FileStream output = System.IO.File.Create(fileWithPath))
await source.CopyToAsync(output);
}
return Ok("Success");
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
public class MyForm
{
public string FirstName { get; set; }
public IList<IFormFile> Files { get; set; }
}
private string EnsureCorrectFilename(string filename)
{
if (filename.Contains("\\"))
filename = filename.Substring(filename.LastIndexOf("\\") + 1);
return filename;
}
private string GetPathAndFilename(string filename)
{
return Path.Combine(_environment.ContentRootPath, "uploadedFiles", filename);
}
}
Full Source Code Repo: https://github.com/rj-learning/DotNetCoreFileUpload
In my case I had to make a POST request, which had information sent through the header, and also a file sent using a FormData object.
I made it work using a combination of some of the answers here, so basically what ended up working was having this five lines in my Ajax request:
contentType: "application/octet-stream",
enctype: 'multipart/form-data',
contentType: false,
processData: false,
data: formData,
Where formData was a variable created like this:
var file = document.getElementById('uploadedFile').files[0];
var form = $('form')[0];
var formData = new FormData(form);
formData.append("File", file);
you can just append them on your formdata, add your files and datas in it.you can read this..
https://developer.mozilla.org/en-US/docs/Web/API/FormData/append
for better understanding. you can separately retrieve them $_FILES for your files and $_POST for your data.
<form id="form" method="post" action="otherpage.php" enctype="multipart/form-data">
<input type="text" name="first" value="Bob" />
<input type="text" name="middle" value="James" />
<input type="text" name="last" value="Smith" />
<input name="image" type="file" />
<button type='button' id='submit_btn'>Submit</button>
</form>
<script>
$(document).on("click", "#submit_btn", function (e) {
//Prevent Instant Click
e.preventDefault();
// Create an FormData object
var formData = $("#form").submit(function (e) {
return;
});
//formData[0] contain form data only
// You can directly make object via using form id but it require all ajax operation inside $("form").submit(<!-- Ajax Here -->)
var formData = new FormData(formData[0]);
$.ajax({
url: $('#form').attr('action'),
type: 'POST',
data: formData,
success: function (response) {
console.log(response);
},
contentType: false,
processData: false,
cache: false
});
return false;
});
</script>
///// otherpage.php
<?php
print_r($_FILES);
?>
I generate a pre-signed URL for uploading files to AWS S3 via a Python-backed API. This part receives filename info once the user selects the file in a browser (see below), and returns a JSON payload (see below) with the base URL and fields.
import logging
import boto3
from botocore.exceptions import ClientError
def create_presigned_post(bucket_name, object_name,
fields=None, conditions=None, expiration=3600):
# Generate a presigned S3 POST URL
s3_client = boto3.client('s3')
try:
response = s3_client.generate_presigned_post(bucket_name,
object_name,
Fields=fields,
Conditions=conditions,
ExpiresIn=expiration)
except ClientError as e:
logging.error(e)
return None
# The response contains the presigned URL and required fields
return response
Here's the JSON response I get from this function. (I changed/abbreviated some of the values, but you get the point.)
{
"url": "https://s3.us-east-2.amazonaws.com/my_bucket_name",
"fields": {
"key": "some.txt",
"AWSAccessKeyId": "ASI...",
"x-amz-security-token": "Ag9o...",
"policy": "eyJ...=",
"signature": "jn...="
}
}
Here's the HTML form that I'm using to upload the file. I have some vanilla Javascript that tracks changes to the form and updates the URL_VALUE for the form and the VALUE for each form item upon changes (e.g. file selection).
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<!-- Copy the 'url' value returned by S3Client.generate_presigned_post() -->
<form action="URL_VALUE" method="post" enctype="multipart/form-data">
<!-- Copy the 'fields' key:values returned by S3Client.generate_presigned_post() -->
<input type="hidden" name="key" value="VALUE" />
<input type="hidden" name="AWSAccessKeyId" value="VALUE" />
<input type="hidden" name="policy" value="VALUE" />
<input type="hidden" name="signature" value="VALUE" />
File:
<input type="file" name="file" /> <br />
<input type="submit" name="submit" value="Upload to Amazon S3" />
</form>
</body>
</html>
This HTML form works fine by itself, but I tried adding some Javascript (both vanilla and JQuery), so I can track file progress and disable form inputs until the upload is complete.
I can't get Javascript to work!!!
I have tried so many examples (again, both vanilla JS and JQuery).
Has anyone recently implemented this and can help?
It's even easier than what you've posted.
fetch(yourSignedUrl, {
method: 'PUT',
body: file,
headers: {
'Content-Type': file.type
}
}).then((res) => {
if (!res.ok) {
throw new Error(res.statusText);
}
return res.headers.get('ETag');
});
I believe you have to pass the AWS secrets such as
"key": "some.txt",
"AWSAccessKeyId": "ASI...",
"x-amz-security-token": "Ag9o...",
"policy": "eyJ...=",
"signature": "jn...="
as headers.
Are you using the fetch library?
Can you please post the JS code to?
I finally figure it out. This is the code.
formData = new FormData()
Object.keys(presignedPostData.fields).forEach(key => {
formData.append(key, presignedPostData.fields[key])
})
file = $('input[type="file"]')[0].files[0]
formData.append("file", file)
fetch(presignedPostData.url, {
method: 'POST',
body: formData,
mode: 'no-cors'
}).then((response) => {
lg('response', response)
})
Alright, found a ridiculously simple vanilla JS example that works!
$(document).ready(function () {
var PRESIGNED_URL = ""
$('input[type="file"]').change(function (e) {
var fileName = e.target.files[0].name;
var settings = {
"async": true,
"crossDomain": true,
"url": "https://my_api.com",
"method": "POST",
"headers": {
"Content-Type": "application/x-www-form-urlencoded",
},
"data": {
"filename": fileName
}
}
$.ajax(settings).done(function (response) {
$("#filename").html(fileName)
PRESIGNED_URL = response["url"]
$("#form").attr("action", response["url"])
$("#key").val(response["fields"]["key"])
$("#AWSAccessKeyId").val(response["fields"]["AWSAccessKeyId"])
$("#policy").val(response["fields"]["policy"])
$("#signature").val(response["fields"]["signature"])
$("#x-amz-security-token").val(response["fields"]["x-amz-security-token"])
return
});
});
$("#button").on("click", function (e) {
var form = document.getElementById('form');
var formData = new FormData(form);
var xhr = new XMLHttpRequest();
// Add any event handlers here...
xhr.upload.addEventListener('progress', function(e) {
var percent_complete = (e.loaded / e.total)*100;
// Percentage of upload completed
console.log(percent_complete);
});
xhr.onloadstart = function (e) {
console.log("start")
}
xhr.onloadend = function (e) {
console.log("end")
}
xhr.open('POST', PRESIGNED_URL, true);
xhr.send(formData);
$("#filename").html("")
})
});
There are so many close variations, but this on worked perfectly.
(I'm sure it seems obvious to many, but I only do front-end dev by necessity...)
I am trying to send an image file to a backend server using a PUT request.
HTML:
<input type="file" name="myFile" id="profilePicInput">
Javacript:
const profileInput = document.getElementById('profilePicInput');
profileInput.addEventListener("change", handleFiles, false);
function handleFiles(){
const formData = profileInput.files[0];
$.ajax({
url: '../uploadImage', //my servlet url
type: 'PUT',
processData: false,
contentType: false,
data: {
data: formData
},
success: function () {
console.log("profile pic updated!");
}
});
}
The problem I'm facing is, in my backend code, I'm receiving data that has a content-type of 'text/plain;charset=UTF-8', but what i need is 'multipart/form-data'.
I've tried two things that didn't work:
Using HTML forms
<form id="profilePicForm" action="javascript:handleFiles()" method="post" enctype="multipart/form-data">
<input type="file" name="myFile" id="profilePicInput">
</form>
Javascript:
const profileInput = document.getElementById('profilePicInput');
profileInput.addEventListener("change", submitForm, false);
function submitForm(){
document.getElementById("profilePicForm").submit();
}
But still the content-type in the request header says 'text/plain;charset=UTF-8' in my PUT request.
Using FormData
function handleFiles(){
const blobFile = profileInput.files[0];
let formData = new FormData();
formData.append("fileToUpload", blobFile);
$.ajax({
url: '../uploadImage', //my servlet url
type: 'PUT',
processData: false,
contentType: false,
data: {
data: formData
},
success: function () {
console.log("profile pic updated!");
}
});
}
Same problem. The content-type in the request header still says 'text/plain;charset=UTF-8' in my PUT request.
So my question is: how do you send a file with content-type=multipart/form-data through a PUT request?
UPDATE:
Solution:
HTML:
<input type="file" name="myFile" id="profilePicInput">
Javascript:
const profileInput = document.getElementById('profilePicInput');
profileInput.addEventListener("change", handleFiles, false);
function handleFiles(){
let formData = new FormData();
formData.append("fileToUpload", profileInput.files[0]);
const xhr = new XMLHttpRequest();
xhr.open('PUT', '../uploadImage');
xhr.onload = () => {
console.log("profile updated");
};
xhr.send(formData);
}
See MDN on Uploading and Downloading Files.
If you want to PUT a file, then you just need to put the file and not wrap it in a multipart/form-data container.
Do not use FormData which is designed to pulling in all the data from a form and not just sending a single file.
const fileToUpload = profileInput.files[0]);
const xhr = new XMLHttpRequest();
xhr.open('PUT', '../uploadImage');
xhr.onload = () => {
console.log("profile updated");
};
xhr.setRequestHeader("Content-Type", fileToUpload.type);
xhr.send(fileToUpload);
Take a look at the documentation: https://api.jquery.com/jquery.ajax/
It states that:
contentType: false,
will not send a content type, I would suggest this means the server recieving is then defaulting. Why not set the type here?
contentType: "multipart/form-data",