I have the following.
<form method="post" action="/send" enctype="multipart/form-data">
<input type="file" name="filename" id="AttachFile">
</form>
I want to change the name of the file the user uploads.
If the user selects "Document.docx" I want to change it to "Bank - Document.docx".
I still want to read the file the user selected, not some other file, just use a different name for it when sending to the server.
I'm working within bounds of an application which doesn't allow control of the server side, so ideally I need to do this on the client. Furthermore I need this to work within the confines of a form.
I have tried variations of the following without success:
document.getElementById("AttachFile").name = "test.txt"
document.getElementById("AttachFile").files = "test.txt"
document.getElementById("AttachFile").value ="test.txt"
You can do it through the File API. We can also use the Blob API to be compatible with Microsoft edge.
var file = document.getElementById("AttachFile").files[0];
var newFile = new File([file], "Bank - Document.docx", {
type: file.type,
});
Here's a complete example — see comments:
HTML:
<input type="file" id="AttachFile">
<input type="button" id="BtnSend" value="Send">
JavaScript:
document.getElementById("BtnSend").addEventListener("click", function() {
// Get the file the user picked
var files = document.getElementById("AttachFile").files;
if (!files.length) {
return;
}
var file = files[0];
// Create a new one with the data but a new name
var newFile = new File([file], "Bank - Document.docx", {
type: file.type,
});
// Build the FormData to send
var data = new FormData();
data.set("AttachFile", newFile);
// Send it
fetch("/some/url", {
method: "POST",
body: data
})
.then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.text(); // or response.json() or whatever
})
.then(response => {
// Do something with the response
})
.catch(error => {
// Do something with the error
});
});
You can't rename the file using a standard form submission. The name of the file being uploaded is read-only. To do this, you'd have to do it server-side. (The designers of file uploads seem to have either not considered this rename-on-upload use case or not felt it needed to be addressed by the API.)
However, you can prevent the default form submission and instead submit it programmatically via ajax, which does allow you to rename the file; see man tou's answer.
If you cannot work on the server side then you have to either rename the file BEFORE upload or AFTER download. How you present the name for the user is you to decide.
Related
I have a form where my members can upload three images for their public profiles.
1.card_front, 2.card-back, 3.art_header
I don't want to use "multiple" on an input field as a member can come back to change 1 or all images at anytime, and I need to know which image has been uploaded - front, back or header.
My JS worked for one image upload but not with multiple single files.
My DB (Xano) gives me a 400 error
{
code: ERROR_CODE_INPUT_ERROR,
message: Value is not properly formed.,
payload: {
param: image1}
}
Xano endpoint expects three inputs that I have created (Type: file resource)
image1, image2, image3
(Xano then creates image metadata from a File Resource)
What does "Value is not properly formed" mean and how to fix? OR can anyone suggest a different way to have three seperate image uploads in a form sent to an API using Fetch?
// POST IMAGES
const memberID = localStorage.getItem('MemberID');
console.log("Member ID:", memberID);
const url = 'XANO endpoint';
document.addEventListener('DOMContentLoaded', init);
function init(){
document.getElementById('btnSubmit').addEventListener('click', upload);
}
function upload(ev){
ev.preventDefault();
//create any headers we want
let h = new Headers();
h.append("Accept", "application/json");
h.append("Content-Type", "multipart/form-data");
//bundle the files and data we want to send to the server
let fd = new FormData();
let myFile1 = document.getElementById('cardFront').files[0];
let myFile2 = document.getElementById('cardBack').files[0];
let myFile3 = document.getElementById('artHeader').files[0];
fd.append("member-id", memberID);
fd.append("image1", myFile1);
fd.append("image2", myFile2);
fd.append("image3", myFile3);
let req = new Request(url, {
method: "POST",
headers: h,
mode: "no-cors",
body: fd
});
fetch(req)
.then( (response)=>{
document.getElementById("output").textContent = "Response received from server";
})
.catch( (err) =>{
console.log("ERROR:", err.message);
});
}
<form action="#">
<div>
<input type="hidden" id="nodes_id" data-name="nodes_id" value=`${memberID}`/>
<input type="file" id="cardFront" accept=".png, .jpg, .jpeg"/>
<input type="file" id="cardBack" accept=".png, .jpg, .jpeg"/>
<input type="file" id="artHeader" accept=".png, .jpg, .jpeg"/>
</div>
<div>
<button class="button" id="btnSubmit">Upload Files</button>
</div>
</form>
Your content-type header is missing the boundary, if you remove that header from your code fetch will set it and add the needed boundary for you.
I'm using Flask with one of my wtforms TextAreaFields mapped to Trix-Editor. All works well except for images using the built toolbar attach button.
I'd like to save the images to a directory on the backend and have a link to it in the trix-editor text. I'm saving this to a database.
I can make this work by adding an <input type='file'/>in my template like so:
{{ form.description }}
<trix-editor input="description"></trix-editor>
<input type="file"/>
and the following javascript which I found somewhere as an example.
document.addEventListener('DOMContentLoaded', ()=> {
let contentEl = document.querySelector('[name="description"]');
let editorEl = document.querySelector('trix-editor');
document.querySelector('input[type=file]').addEventListener('change', ({ target })=> {
let reader = new FileReader();
reader.addEventListener('load', ()=> {
let image = document.createElement('img');
image.src = reader.result;
let tmp = document.createElement('div');
tmp.appendChild(image);
editorEl.editor.insertHTML(tmp.innerHTML);
target.value = '';
}, false);
reader.readAsDataURL(target.files[0]);
});
// document.querySelector('[role="dump"]').addEventListener('click', ()=> {
// document.querySelector('textarea').value = contentEl.value;
// });
});
This saves the image embedded in the text. I don't want that because large images will take up a lot of space in the database and slow down loading of the editor when I load this data back into it from the database.
It is also ugly having the extra button when Trix has an attachment button in it's toolbar. So, I'd like to be able to click the toolbar button and have it upload or if that is too hard, have the built in toolbar button save the image embedded.
To save the images to a folder instead of embedded, the Trix-editor website says to use this javascript https://trix-editor.org/js/attachments.js
In this javascript I have to provide a HOST so I use
var HOST = "http://localhost:5000/upload/"
and I set up a route in my flask file:
#tickets.post('/_upload/')
def upload():
path = current_app.config['UPLOAD_DIRECTORY']
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
session["id"] = filename
file.save(os.path.join(path, filename))
return send_from_directory(path, filename)
I can select an image and it shows in the editor and it uploads to the directory on my backend as expected. But when I save the form the location of the image is not in in the document text (should be in there as something like <img src="uploads/image.png>
On the python console I see
"POST /_upload/ HTTP/1.1" 404 -
I can make this go away if I change the return on my route to something like return "200" But all the examples I have seen about uploading files have this or a render_template. I don't want to render a template so I'm using this although I don't really understand what it does.
I'm assuming I need to return something the javasript can use to embed the image link in the document. But I'm a total newbie (like you didn't figure that out already) so I don't know what to do for the return statement (assuming this is where the problem lies).
If anyone else is trying to figure this out this is what I ended up doing.
Still needs a but of tweaking but works.
First I modified the example javascript for uploading to use Fetch instead of XMLHttpRequest
const editor = document.querySelector('trix-editor');
(function() {
HOST = '/_upload/'
addEventListener("trix-attachment-add", function(event) {
if (event.attachment.file) {
uploadFileAttachment(event.attachment)
}
// get rid of the progress bar as Fetch does not support progress yet
// this code originally used XMLHttpRequest instead of Fetch
event.attachment.setUploadProgress(100)
})
function uploadFileAttachment(attachment) {
uploadFile(attachment.file, setAttributes)
function setAttributes(attributes) {
attachment.setAttributes(attributes)
alert(attributes)
}
}
function uploadFile(file, successCallback) {
var key = createStorageKey(file)
var formData = createFormData(key, file)
fetch(HOST, {method: 'POST', body: formData}).then(function(response){
response.json().then(function(data){
alert(data.file, data.status)
if (data.status == 204) {
var attributes = {
url: HOST + key,
href: HOST + key + "?content-disposition=attachment"
}
console.log(attributes)
successCallback(attributes)
}
})
})
}
function createStorageKey(file) {
var date = new Date()
var day = date.toISOString().slice(0,10)
var name = date.getTime() + "-" + file.name
return [day, name ].join("/")
}
function createFormData(key, file) {
var data = new FormData()
data.append("key", key)
data.append("Content-Type", file.type)
data.append("file", file)
return data
}
})();
Then modified my Flask route (which I'll refactor, this was just slapped together to make it work):
def upload():
path = current_app.config['UPLOAD_DIRECTORY']
new_path = request.form["key"].split('/')[0]
file_upload_name = os.path.join(path, request.form["key"])
print(file_upload_name)
upload_path = os.path.join(path, new_path)
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
if not os.path.exists(upload_path):
os.mkdir(upload_path)
filename = secure_filename(file.filename)
session["id"] = filename
attachment = os.path.join(upload_path, filename)
file.save(attachment)
file.close()
os.rename(attachment, file_upload_name)
print(os.listdir(upload_path))
return jsonify({'file': attachment, 'status': 204})
return f'Nothing to see here'
Anyway, I hope that helps as it took me ages to figure out.
I have a form that contains article information and images that I submit using the DropZone library.
I have no problem with this library and it works very well, but when the submitted form had an error and I receive this error message on the client side via Ajax, the user fixes the problems and sends the form again, but unfortunately the form is not sent and no file is left. Not selected
While the files are available in the preview and are sent to the server only once.
What should I do to solve this problem?
Please enter simple codes.
Thanks
successmultiple function
myDropzone.on("successmultiple", function(file,serverResponse) {
/* None of the uploaded files are available in Drop Zone here anymore,
** and I had to delete the files so the user could choose again,
** which would not be a good user experience.
** Exactly what code should I write here so that there is no need to
** re-select files from the user's system?
*/
myDropzone.removeFile(file);
if(serverResponse.status)
{
// Success:: In this case, I have no problem
alert("Article saved successfully. Redirecting to the Articles page ...");
window.location.href = serverResponse.redirectedTo;
}
else
{
// Display errors received from the server to the user
alert("Please enter your name and resubmit the form.");
}
});
I think a possible solution is if you pass the event to your successhandler and prevent it from its default bahaviour.
Like so:
function successHandler(event){
event.preventDefault();
}
This should prevent it refreshing the page and loosing the file in the input.
Otherwise I would just save the file to a variable.
I found the answers to my questions myself and i will put it below for you too.
This code is written for Laravel blade file :
<script>
$("document").ready(()=>{
var path = "{{ $path }}";
var file = new File([path], "{{ $attach->file_name }}", {type: "{{ $attach->mime_type }}", lastModified: {{ $attach->updated_at}}})
file['status'] = "queued";
file['status'] = "queued";
file['previewElement'] = "div.dz-preview.dz-image-preview";
file['previewTemplate'] = "div.dz-preview.dz-image-preview";
file['_removeLink'] = "a.dz-remove";
file['webkitRelativePath'] = "";
file['width'] = 500;
file['height'] = 500;
file['accepted'] = true;
file['dataURL'] = path;
file['upload'] = {
bytesSent: 0 ,
filename: "{{ $attach->file_name }}" ,
progress: 0 ,
total: {{ $attach->file_size }} ,
uuid: "{{ md5($attach->id) }}" ,
};
myDropzone.emit("addedfile", file , path);
myDropzone.emit("thumbnail", file , path);
// myDropzone.emit("complete", itemInfo);
// myDropzone.options.maxFiles = myDropzone.options.maxFiles - 1;
myDropzone.files.push(file);
console.log(file);
});
</script>
We're trying to upload a song (.mp3) file from a JSP frontend written in HTML / Javascript. We need to upload to our Java backend using websockets. Does anyone have any suggestions on how we would could go about doing this?
Currently we are doing something like this on our JSP file:
<h1>Please Choose a Song file</h1>
<form name = "JSONUploadForm">
<input type = "file" name="file" accept = ".mp3"/> <br/>
<input type = "button" value = "Click to upload!" name = "button" onClick = "submitSong();"/>
</form>
Then we have our javascript function submitSong()
function submitSong(){
var songStuffs = document.getElementById("file");
console.log(songStuffs); --> we get "null" here
sendMessage(songStuffs);
alert("song sent");
}
function sendMessage(val, string) {
socket.send(string);
return false;
}
Also, here is our connect to server function. However, this functions correctly.
function connectToServer() {
socket = new
WebSocket("ws://localhost:8080/Project/socket");
socket.onopen = function(event) {
console.log("connected!");
}
You can also see our server side (.java file):
#OnMessage
public void onMessage(String message, Session session) throws IOException, EncodeException {
FileWriter fw = new FileWriter(new File(songName + ".mp3"));
fw.write(song);
BufferedReader fr = new BufferedReader(new FileReader(songName + ".mp3"));
String data = fr.readLine();
System.out.println("Song: " + data); --> Here we get "song: null"
}
Any suggestions would be greatly appreciated!
In your code you have an error
"var songStuffs = document.getElementById("file");"
Your file input without id.
this will work "var songStuffs = document.querySelector("[name=file]");"
I prefer using querySelector, because it mo flexeble and works exactly like jquery query selectors)))
You do not need any form, for upload files.
Please read this article https://www.html5rocks.com/en/tutorials/websockets/basics/,
it will be useful for you (search words "blob" at the page)
Html
<input id="file" type = "file" name="file" accept = ".mp3"/>
Code
var fileInput = document.querySelector("#file");
fileInput.addEventListener("change",function(){
connection.send(fileInput.files[0]);
});
If you need to send file and fields, you have 3 variants
create JSON {"field1":"value1","field2":"value1",
"file":"base64"}
manualy create formdata and parse form data at the
server with some webform stuff (example
https://stackoverflow.com/a/47279216/5138198)
Firstly send JSON
data, secondly send a file
Try with this, If you have to upload file you should add enctype in form.
<form enctype="multipart/form-data">
<input type = "file" name="file" id="song" accept = ".mp3"/>
</form>
Update:
You can use simply WebSocketFileTransfer plugin to send your file. This plugin helps in with many features like Auth, progress status, file/blob compatibility.
var songStuffs = document.getElementById("song").files[0];
var transfer = new WebSocketFileTransfer({
url: 'ws://ip:port/path/to/upload_web_socket_server',
file: songStuffs,
progress: function(event) {
// Update the progress bar
},
success: function(event) {
// Do something
}
});
transfer.start();
In my page I had only one html file input :
input type="file" name="file" files-model="service.file" id="filo" accept=".zip" />
And I used to get my file to be uploaded in a multi-part file request like this :
var fd = new FormData();
fd.append('file', filo.files[0]);//filo is the id of the input
//and send my http request having fd as a parameter
The problem is that now I have auto generated forms in my page with angular ng-repeat so I can no more get my file using the input ID, how can I do this now ?
Cheers,
It will be a bit tricky with javascript. this library worked for me. see example below.
<a class="btn btn-sm btn-primary"
type="file"
ngf-select="receiveSlectedFiles($file)">
Select Logo
</a>
whenever you select file, you will get it in receiveSlectedFiles($file) function. when user select valid file it will be in $file if not the $file will be null.
example in controller:
var arrayOfFiles = [];
$scope.receiveSlectedFiles = function (file) {
if (file) {
arrayOfFiles.push(file);
}
};
In service, inject Upload and then use below code to upload image:
this.uploadMultiple = function (files) {
var uri = yourApiUrl;
return Upload.upload({
url: uri,
arrayKey: '',
data: {
files: files
}
})
};
in controller, you just need to pass array of files to this service.