Upload bucket s3 da form html

0

Hi, as the title suggests, I'm trying to upload a file (png) to a bucket from an html page. I have set up an api via api gatewey which given the file name as input returns me a "presigned url" and so far everything works if I use the presigned url from postman it works correctly but if I submit the form it doesn't work and it returns the mistake: <Error> <Code>InvalidArgument</Code> <Message>Malformed Unicode code sequence in the field.</Message>

this is my html page::

<!DOCTYPE html>
<html>
 <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  </head>
<body>
<p>Click on the "Choose File" button to upload a file:</p>
<form id="uploadForm"  method="post" action="https://my-bucket-bucket.s3.amazonaws.com" enctype="multipart/form-data" >
	<input type="text" id="X-Amz-Algorithm"   name="X-Amz-Algorithm" value="" />
    <input type="text" id="X-Amz-Credential"  name="X-Amz-Credential" value="" />
    <input type="text" id="X-Amz-Date"   name="X-Amz-Date" value="" />
	<input type="text" id="X-Amz-Expires" name="X-Amz-Expires" value="" />
	<input type="text" id="X-Amz-SignedHeaders" name="X-Amz-SignedHeaders" value="" />
	<input type="text" id="X-Amz-Security-Token" name="X-Amz-Security-Token" value="" />
	<input type="text" id="X-Amz-Signature" name="X-Amz-Signature" value="" />
   <input type="file" id="myFile" name="filename">
	
  <input type="submit">
</form>

<script type="text/javascript">
         document.getElementById('myFile').addEventListener('change', function(e) {
		  if (e.target.files[0]) {
			document.body.append('You selected ' + e.target.files[0].name);
			document.body.append('You selected ' + e.target.files);
		  }
		  //get presignedUrl
		  
		const xhr = new XMLHttpRequest();
		xhr.open("POST", "https://myapigateway.execute-api.eu-west-1.amazonaws.com/DEV");
		xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
		xhr.setRequestHeader("Access-Control-Allow-Origin", "*");
		
		var obj="";
		var url1 ="";
		  const body = JSON.stringify({
				fileName:e.target.files[0].name
			});
		xhr.onload = () => {
		  if (xhr.readyState == 4 && xhr.status == 201) {
			console.log(JSON.parse(xhr.responseText));
		  } else {
			console.log(`Error: ${xhr.status}`);
			console.log(xhr.responseText);
			obj=JSON.parse(xhr.responseText);
			url1 =new URL(obj.url);
			console.log(url1);
			const searchParams = url1.searchParams;
			console.log(searchParams);
			document.getElementById("key").value = url1.pathname;
			document.getElementById("X-Amz-Algorithm").value = searchParams.get('X-Amz-Algorithm');
			document.getElementById("X-Amz-Credential").value = searchParams.get('X-Amz-Credential');
			document.getElementById("X-Amz-Date").value = searchParams.get('X-Amz-Date');
			document.getElementById("X-Amz-Signature").value = searchParams.get('X-Amz-Signature');
			document.getElementById("X-Amz-SignedHeaders").value = searchParams.get('X-Amz-SignedHeaders');
			document.getElementById("X-Amz-Expires").value = searchParams.get('X-Amz-Expires');
			document.getElementById("X-Amz-Security-Token").value = searchParams.get('X-Amz-Security-Token');
			
		  }
		};
		xhr.send(body);  
});
    </script>
</body>
</html>

as you can see from the code through an eventListener on file selection I call the api gateway I get the presigned url with all the info and I go to set it in the form but when I submit it doesn't work. I have tried various solutions suggested by the web for similar problems (identical in the error message) but in my case they have not been resolved.

anyone had a problem like this? can you help me?

Thanks in advance

4 Answers
0
Accepted Answer

thank you but I have already the presigned url (with postman work correctly), my presigned url has this form:

https://my-bucket.s3.amazonaws.com/immage.png? X-Amz-Algorithm=AWS4-HMAC-SHA256& X-Amz-Credential=ASIA5FPAP6OKGJZUPSGB%2F20230402%2Feu-west-1%2Fs3%2Faws4_request& X-Amz-Date=20230402T212500Z& X-Amz-Expires=180& X-Amz-SignedHeaders=host& X-Amz-Security-Token=IQoJb3JpZ2luX2VjEO7%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCWV1LXdlc3QtMSJIMEYCIQDdEAuDddeae%2BQcKLyFT8dGLRp0vp37vFLJZwzZhTb2BAIhAJXIXvjy8ZegRiMctoJva2YORROUOEmnlqtHJ7%2BchanIKvsCCMb%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQARoMOTA1MDk4Mjk0MTY0Igwdqj4opphPo1tmeGoqzwI1VpSf5w8elnFZN%2FiXVU5hJtysw9BmCtZmRPFi7jXsiOcWVVTmeqjeGRBJcThF%2FjNSlRTUDeruF%2BWTLmEKI%2BYxyauAn6aIlCJddPovxgUONyn9%2FfkQ%2BBSRjTeJmtjmILlhPbQy1nXHZTfTKvDjvuK9VsUoKN6OnzbAueW5YXx4%2BoapUxmHa51nG%2FIL92Ej1R%2FTF7f1mdlinToz0LdM9spzph0d5gUXHEat8bV%2BMfwtMGyI4Kt48ddndu%2BHmBay18%2Bq84geSwkCWJofQnVRQzf8s%2B3RpIieRkMaB24OB2lawuz2ml3BitUmz87NTQ5Cwrzq29BH33oH31Q9GcdccaURATIOlSPV%2FKDNoY251WA8pmbwsnD7ZUus9yRkx6NXStM0gpqaKZ9ENtEgzrtbzy06N70jcL8ihoyieJKAV5jXiU0gQqpi7a%2F2ntppbF9XyzCS4qehBjqdAT%2BWp7e10U5uX%2FV8E82tKfQwGi6BazQE2NTHti8tvoeWxVmWKMMdXRHOPmToE3STEnS2J2WtJGxoypI18fq0hVm4dOTsWxEJgAaQceT9aalJt6w5%2Frc380NacUAOMZgeQoWMmj3PJ9WEJ%2BExG6b%2Fha%2BoV2AnMHerNe8ck07Shchbs7bT0kuutZQF90sM6spoK%2Bk8QKmY%2BexsQRscKeM%3D& X-Amz-Signature=2d5392ce973f34e5097e4b0b24bcc965317528f5e0bf5567b12e3b183a158874

I use this presigned url to compose my form data, as you can see in the my presigned url is not present an access token. what do you mean by access token? the presigned url should already be complete like this.

answered a year ago
  • In my code, accessToken is how I authenticate to my service to get a presigned URL. You can ignore it - it isn't important in this example, it's just a way to get the presigned URL. If your URL is working in Postman and not in your code then you need to find out what the code is doing differently to Postman.

0

This is postman with the same url: Postman with query params Postman body

this is my form submitted: Header Payload

Postman is put and has the variable as params mentre my form is post but i followed this guide "https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html" if i try to modify my form in put i obtain another error: Inserisci qui la descrizione dell'immagine

answered a year ago
  • The pre-signed URL and the actual S3 call have to match exactly in terms of parameters. That error is saying that there is a header or other parameter which is either being supplied and isn't in the pre-signed URL; or it is in the pre-signed URL and isn't being supplied in the API call.

0

Here's some (lightly edited) code that I use - it's jQuery-based but I think it should make sense:

const file = $('#uploadfile');
fileObject = file.prop('files')[0];

var reader = new FileReader();
reader.readAsArrayBuffer(fileObject);
reader.onload = function() {
formData = {'name':fileObject.name, 'contentType':fileObject.type};

$.post({
  url: apiEndpoint+'getUploadToken',
  headers: {'token':accessToken}, // accessToken is not relevant, it's just a way to get the presigned URL
  data: JSON.stringify(formData),
  dataType: 'json'
}).then(function(url) { // getUploadToken returns the presigned URL
  $.ajax({
    type: 'PUT',
    url: url,
    processData: false,
    contentType: fileObject.type,
    data: reader.result
  }).then(function(s3data) {
    // Success
  }).fail(function(s3data) {
    // Upload fail
  });
}).fail(function(data) {
  // getUploadToken fail
});
profile pictureAWS
EXPERT
answered a year ago
0

I fixed it with a javascript function and adding this " "AllowedOrigins": ["*"]" to the CORS configuration. So I do the "submit" from the javascript function, evidently the html form differed in something.

<script>
  
	const imageForm = document.querySelector("#imageForm")
	const imageInput = document.querySelector("#imageInput")
	imageForm.addEventListener("submit", async event => {
	  event.preventDefault()
	  const file = imageInput.files[0]
	  var  url  = ""
	   //retrieve presigned url
	  await fetch('<api gateway that return presigned URL>',{
		  method: 'POST',
		   headers: {
			'Accept': 'application/json, text/plain, */*',
			 'Access-Control-Allow-Origin':'*',
			'Content-Type': 'application/json'
		  },
		  body: JSON.stringify({fileName:file.name})
		}).then(res => res.json())
		.then(res => {url=res.url})

	  // post the file direclty to the s3 bucket
	  await fetch(url, {
		method: "PUT",
		headers: {'Content-Type':'application/json',
                    'Access-Control-Allow-Origin':'*',
                    'Access-Control-Allow-Methods':'POST,PATCH,OPTIONS'},
		body: file
	  })

	  const imageUrl = url.split('?')[0]
	 location.reload();
	})
  </script>
answered a year ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions