Uploading image using the <img attribute - javascript

I'm trying to see if it is possible to upload an image from the <img src=""> to a database, without using the input function
<form class="form-horizontal" validate="true" action="upload1.php" method="POST" enctype="multipart/form-data">
<a href="javascript:void(0)" class="closebtn" onclick="closeForm()"</a>
<img id="output" src="name.png" data-name="photo" width="100%"/>
<input type="hidden" name="photo" id="output" value="">
<input type="submit" name="reg" value="Update Status" class="btn btn-solid">
</form>
I just want to know if there's a way it could work

I am not sure why you do not want to use an input field, since this makes the image upload easier and also provides you a File object which inherits from a Blob and provides you info about the image like filename, lastModified, etc (see more here).
Regardless, yes this is possible in javascript using FormData. However, you would have to either convert the image to base64 (which I do not recommend because this makes the size about 33% larger), or use a Blob object and upload that (which also involves a hacky workaround using fetch). Here is an example of what you could do.
// Convert img to Blob
const img = document.getElementById('output');
fetch(img.src)
.then(res => res.blob())
.then(blob => {
// Prepare image for upload
const fd = new FormData();
fd.append('uploadedImage', blob, 'tmp.png');
// Upload
fetch(<image-upload-endpoint>, {
method: 'POST',
body: fd
})
.then(res => {
if (res.ok) {
// Upload was successful
} else {
// Upload failed
}
});
});
Then you would be able to retrieve the file in PHP with $_FILES['uploadedImage']

Related

How to show Toast message after successfully uploading file?

I write up a program that asks user to upload file using Google App Script (see below link).
https://script.google.com/macros/s/AKfycbyd3bqAxJ3QVb2NzNdl8Hwjsl8nuOh7IOtWzlcH_XR1iToBEhdLAfMhM19S24EGinZi/exec
Currently, the script works and users can upload their file successfully to my Google drive.
However, I would like to show the Toast message (e.g., File uploaded, thank you!), so user can see it after click the submit button.
Could someone please help me to edit the code below ?
Thank you
code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile("form.html");
}
function doPost(e) {
const folderId = "1v70Nw93iVglTRSb6f4ICGoZoHSRKq2y7"; // Or Folder ID which is used for putting the file instead of "root", if you need.
const blob = Utilities.newBlob(JSON.parse(e.postData.contents), e.parameter.mimeType, e.parameter.filename);
const file = DriveApp.getFolderById(folderId).createFile(blob);
const responseObj = {filename: file.getName(), fileId: file.getId(), fileUrl: file.getUrl()};
return ContentService.createTextOutput(JSON.stringify(responseObj)).setMimeType(ContentService.MimeType.JSON);
}
<form id="form">
<label for="filename"> 1. 請點選以下 『選擇檔案』 上傳您的 pdf 摘要檔案 (Please upload your pdf file):</label>
<p>
<input name="file" id="uploadfile" type="file">
<p>
<hr>
<p>
<label for="filename">2. 請輸入您的姓名 (Your name):</label>
<p>
<input name="filename" id="filename" >
<p>
<hr>
<p>
<label for="filename">3. 請點選以下 『提交』 按鈕 (Click Submit):</label>
<p>
<input id="submit" type="submit">
<p>
<hr>
<p>
<label for="submit">註: 點選 『提交』 10秒後即完成上傳,可關閉此頁面, 謝謝您。 <p>
Note: File will be successfully uploaded in 10 seconds, then you can close this page, thank you.</label>
</form>
<script>
const form = document.getElementById('form');
form.addEventListener('submit', e => {
e.preventDefault();
const file = form.file.files[0];
const fr = new FileReader();
fr.readAsArrayBuffer(file);
fr.onload = f => {
const url = "https://script.google.com/macros/s/AKfycbxr8nvfm1f4dy1NxOqHxNJlVzQmkAmSPjaqgtFwZ5KHVA1Zfxx0vLllFEAGGZF0yH_5/exec"; // <--- Please set the URL of Web Apps.
const qs = new URLSearchParams({filename: form.filename.value || file.name, mimeType: file.type});
fetch(`${url}?${qs}`, {method: "POST", body: JSON.stringify([...new Int8Array(f.target.result)])})
.then(res => res.json())
.then(e => console.log(e)) // <--- You can retrieve the returned value here.
.catch(err => console.log(err));
}
}
);
</script>

React.js Previewing an Image with input type file

Currently I am trying to preview an image up on an upload of an image file. I can recieve the image, and console log however I can not display it in the img tag.
Here is the way that I am trying to render them:
<input type='file' id="uploadImage" onChange={this.handleImageChange} className="upload-input" />
<label htmlFor="uploadImage" className="upload-image-button">Choose An Image</label>
{this.state.imagePreview[0] ?
<img src={this.state.imagePreview[0]} alt="" className="image-preview" />
:
<img alt="" className="image-preview" />
}
Here are my state and my handle on change method for the input:
state = {
imagePreview: {}
}
handleImageChange = async (e) => {
console.log(e.target.files)
await this.setState({ imagePreview: e.target.files })
console.log(this.state.imagePreview)
}
Here are my console logs for the files:
You'll need to use a FileReader object, in order to get the base64 data and apply it to the source of the image tag.
Also, I don't see any need for this to be an async function (unless, for the sake of brevity, you left out a promise you're working with).
From the code you've presented, something like this should work:
handleImageChange(event) {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = function(event) {
// The file's text will be printed here, if you want to see the base64
console.log(event.target.result)
// Set state with base64 data
this.setState({ imagePreview: e.target.result })
};
reader.readAsText(file);
}
Html (or jsx) would look something like so:
<img src={this.state.imagePreview} alt="" className="image-preview" />

Netlify form fetching a GET instead of POST request on submit

I have a basic Netlify form (based on this guide) with name, email and message fields. With the following submit function:
const handleSubmit = event => {
event.preventDefault();
const data = {};
const scopedForm = [...formState];
let isValidForm = validateForm(scopedForm);
setFormState([...scopedForm]);
if (!isValidForm) return false;
formInputs.forEach(input => data[input.name] = input.value);
fetch(`/`, {
method: `POST`,
headers: {
'Accept': `application/x-www-form-urlencoded;charset=UTF-8`,
'Content-Type': `application/x-www-form-urlencoded`,
},
body: encode({
'form-name': `Contact Form`,
...data,
}),
})
.then(() => console.log(`OK`))
.catch(error => alert(error));
};
const encode = data => {
return Object.keys(data)
.map(key => encodeURIComponent(key) + `=` + encodeURIComponent(data[key]))
.join(`&`);
};
Pretty simple, besides the validations, I create a data object and I fill it with a pair of data[input.name] = input.value. Everything works as expected locally, as well as in develop and build mode. I can see a POST request, however, in production, it turns into a GET:
I've tried changing the built-in fetch to axios but the result is the same. I don't know if I need to add some custom configuration in my server or how to bypass this.
My resulting HTML structure is:
<form name="Contact Form" method="POST" action="/" data-netlify="true" data-netlify-honeypot="bot-field" data-netlify-recaptcha="true">
<div><label for="form-name"><input type="hidden" name="form-name" value="Contact Form"></label></div>
<div><label for="bot-field"><input type="hidden" name="bot-field" value=""></label></div>
<div><label for="name">Name:<input type="text" name="name" value="Chancellor Lawson"></label></div>
<div><label for="email">Email:<input type="text" name="email" value="fivyhohy#mailinator.com"></label></div>
<div><label for="message">Message:<textarea name="message">Ea quisquam ea vel e</textarea></label></div>
<button type="submit">Send</button>
</form>
I have read a lot of similar issues, articles, and guides but none helped.
In order to close the issue, I will answer my own question, giving all the merits to Quentin. As he pointed out, the solution was removing the Accept header since it only was accepting application/x-www-form-urlencoded;charset=UTF-8 requests. So the header should look like:
headers: {
'Content-Type': `application/x-www-form-urlencoded`,
},
From MDN documentation:
The Accept request HTTP header advertises which content types,
expressed as MIME types, the client is able to understand. Using
content negotiation, the server then selects one of the proposals,
uses it and informs the client of its choice with the Content-Type
response header. Browsers set adequate values for this header
depending on the context where the request is done: when fetching a
CSS stylesheet a different value is set for the request than when
fetching an image, video, or script.

Uploading images using aurelia to asp.net core backend

I have been searching for a solution for this but none of the guides are updated or suited for my intention. I need a user uploaded image to be loaded into javascript/aurelia which then sends it to the asp.net core backend using its http fetch client so the image is saved on disk(not in a database). I'm currently using the following code but I'm getting the following error and no images are being saved.
extract from html code being used to upload image
<input class="hiddenButton" id="images" type="file" accept=".jpeg" file.bind="image">
<button class="upload" onclick="document.getElementById('images').click()">
<i class="fa fa-pencil" style="color:green"></i>
</button>
extract of javascript code used to invoke saving
save() {
this.api.saveEmployee(this.employee).then(employee => this.employee = employee);
this.ea.publish(new EmployeeAdded(this.employee));
this.api.saveImage(this.image);
return this.employee;
}
Javascript/aurelia code
saveImage(image) {
var form = new FormData()
form.append('image', image)
this.http.fetch('/api/Images', {
method: 'POST',
//headers: { 'Content-Type': image.type },
body: form
})
.then(response => {
return response
})
.catch(error => {
console.log("Some Failure...");
throw error.content;
})
return true;
}
Asp.net core MVC code(backend)
[HttpPost]
public async Task<IActionResult> SaveImage(IFormFile file)
{
Console.WriteLine("Images controller");
var filePath = Path.Combine(Directory.GetCurrentDirectory(),"Image");
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return Ok();
}
error message
The HTML element <input type="file" /> does not have a property file, the correct property is files, so it sounds like the problem is with aurelia/javascript and binding.
Since the property files is a FileList (collection) you will need to access the first file in the collection. Even though you haven't used multiple I think files will still be a collection.
You could try this:
// html
<input class="hiddenButton" id="images" type="file" accept=".jpeg" files.bind="image">
// ^ files
// jss/aurelia
saveImage(image) {
var form = new FormData();
form.append('image', image[0]); // access the first image in collection image[0]
// the other code remains the same
//...
}
PS I haven't used aurelia so can't be 100% sure this is the issue but hopefully points you in the correct direction.
PPS: since files is a collection, technically image in your view model is a collection too, so you could consider renaming it to images to make it clearer (even if you're only using one image). It should still work using image[0] but images[0] would be clearer.

jquery Return to same screen without refreshing screen

I have an upload view that needs to be used to upload three attachments. Now I used this code for the UI part in the view:
<div id="theDeliveryNoteContent">
<form action='Order/Save' method="post" enctype="multipart/form-data" id="deliveryNoteForm">
<div >
<label style="text-align: left;">Delivery note:</label>
<input type="file" name="DeliveryNoteFile" id="DeliveryNote" style="width: 400px;" />
<div style="margin-top:4px;margin-bottom:4px" >
<input type="submit" value="Upload" id="btnAddAttachment" />
</div>
</div>
</form>
</div>
Now the method that I want to call is situated inside my Orders controller. Here is the method I'm using. The code works fine until the return part.
[HttpPost]
public ActionResult Save(HttpPostedFileBase DeliveryNoteFile)
{
try
{
string customer = GetCustomerLinkedToPortalUser();
var uploadPath = "C:\\Attachments\\" + customer;
if (!Directory.Exists(uploadPath))
{
Directory.CreateDirectory(uploadPath);
}
if (DeliveryNoteFile != null)
{
var fileName = Path.GetFileName(DeliveryNoteFile.FileName);
var physicalPath = Path.Combine(uploadPath, fileName);
DeliveryNoteFile.SaveAs(physicalPath);
}
return RedirectToAction("Index");
}
catch (Exception)
{
throw;
}
}
The problem is that when the method returns to the screen it refreshes the screen and all the entered information is lost. I want to save the file to that directory and come back to the order screen and upload the next file. Now how I'm supposed to do that I'm not sure so that is what I need help with.
A colleague mentioned that I could use jQuery.Form script to do an ajax call so what I did is I added the jquery.form.js script to my project, did the referencing and I also added this to my javascript:
$("#deliveryNoteForm").ajaxForm({
target: "#theDeliveryNoteContent"
});
So now it returns to my screen, but it messes up the layout and refreshes the screen (seems) anyway. Is there any other easy way to return to the previous screen with the method which I used without losing all the entered information?
you need async file upload. Use this. Read some docs it is all simple.
Example:
Javascript initialize:
$(function () {
$('#DeliveryNote').fileupload({
dataType: 'json',
done: function (e, data) {
$.each(data.result.files, function (index, file) {
$('<p/>').text(file.name).appendTo(document.body);
});
}
});
});
Html:
<input id="DeliveryNoteFile" type="file" name="files[]" data-url="yourUploadController/Save/" style="width: 400px;" />
and remove submit button.

Categories