How to show Toast message after successfully uploading file? - javascript

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>

Related

I can't get my Post request to have the values I think they should have

I did a build along to make a twitter tweet box. It has html css and js. The character count portion seems to work great. But when I try to send the value of the text area as a POST request, the json payload comes back as None. When I hit the server from Postman, it seems to work just fine. I don't know how much of the code I should include. So please let me know. I don't want to put the whole thing and make it unreadable. I'll include the parts I think make sense to include.
<div class="wrapper">
<form action="{{ url_for('thread_builder') }}" method="post">
<div class="input-box">
<div class="tweet-area">
<span class="placeholder">What's happening?</span>
<div class="input editable" contenteditable="true" spellcheck="false"></div>
<div id="text" class="input readonly" contenteditable="true" spellcheck="false"></div>
</div>
const textarea = document.getElementById("tweet-area");
const button = document.getElementById("tweetButton");
const counter = document.getElementById("counter");
const readonlyInput = document.getElementById("readonlyInput");
const maxLength = 280;
button.addEventListener("click", function() {
let text = textarea.value;
let currentLength = text.length;
if (currentLength <= maxLength) {
// send text to the server using fetch
fetch('/thread_builder', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ "text": text })
})
.then(response => response.json())
.then(data => {
// handle the response from the server here
})
.catch(error => {
// handle error here
});
}
I tried sending the POST request from POSTMAN and it worked great. I've tried check what type of information is being sent, and I was expecting a string with what was entered in the text box.enter code here

Uploading image using the <img attribute

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']

Event firing multiple times in Vuejs

So, I wrote a function that using which the user can upload multiple files.
<input multiple type="file" id="inputFile" ref="fileSelect" #click="uploadfiles"/>
uploadfiles(){
'use strict'
const inputFile = document.getElementById('inputFile')
console.log(inputFile)
inputFile!.addEventListener('change',async e => {
const files = (e.target as HTMLInputElement).files
const reader = new FileReader()
console.log(files)
console.log(reader)
// New set of results each time.
this.results.splice(0)
for (const file of files!){
if(file["type"] == "application/json"){
this.results.push(await this.readjsonfile(reader, file))}
else if(file["type"]=="text/csv"){
this.results.push(await this.readcsvfile(reader, file))
}
}
})}
Now when I upload a file for the 1st time it works fine, but when I click on the choose files button again to upload another file, it uploads that file twice.
I think the problem is that when I click again on choose files I "activate" the uploadfiles function again, which causes the Event Listener to fire twice, but I am not sure how to do it any other way.
I am new to JavaScript and vuejs, so any suggestions are greatly appreciated.
Thanks to #Daniel I finally ended up figuring out how to fix this.
My final code is
<input multiple type="file" id="inputFile" ref="fileSelect" #change="uploadfiles"/>
uploadfiles(event){
'use strict'
console.log(event)
//console.log(event.target.files)
const q = [event.target.files]
q.forEach(async w => {
const reader = new FileReader()
this.results=[]
for(const file of w){
if(file["type"] == "application/json"){
this.results.push(await this.readjsonfile(reader, file))}
else if(file["type"]=="text/csv"){
this.results.push(await this.readcsvfile(reader, file))
}
}
})
console.log(this.results)
}
The reason is that you're adding inputFile!.addEventListener every time the user clicks on the input form
Instead, if you were to use #change, instead of #click, that should not be happening any more.
example:
<script setup>
import {ref} from "vue"
let files = ref([])
function uploadfiles(event){
files.value.splice(0);
[...event.target.files].forEach(async file => {
// do the logic here
files.value.push(file)
})
}
</script>
<template>
<input multiple type="file" id="inputFile" ref="fileSelect" #change="uploadfiles"/>
<pre>{{files}}</pre>
</template>
SFC playground

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" />

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.

Categories