Ajax file downloads with same name - javascript

This is my Fiddle: https://jsfiddle.net/e6b5hdow/2/
const links = document.querySelectorAll('[href$=".jpg"], [href$=".png"], [href$=".mp4"], [href$=".avi"], [href$=".jpeg"], [href$=".mkv"], [href$=".csv"]');
links.forEach(link => link.classList.add('download-button'));
$('.name').prepend($("<div class='download-ui-container'><div class='start-download'>Starting Download..</div><div class='download-progress-container'><div class='download-progress'></div></div><a class='save-file'>Save File</a></div>"));
var _OBJECT_URL;
$(document).on('click', '.download-button', function(event) {
var request = new XMLHttpRequest();
fileDownload = $(this).attr('href');
var _t = $(this);
request.addEventListener('readystatechange', function(e) {
if (request.readyState == 2 && request.status == 200) {
_t.parents('td').find('.start-download').css('display', 'block');
_t.parents('td').find('.download-button').hide();
} else if (request.readyState == 3) {
_t.parents('td').find('.download-progress-container').css('display', 'block');
_t.parents('td').find('.start-download').hide()
} else if (request.readyState == 4) {
_OBJECT_URL = URL.createObjectURL(request.response);
var fileName = fileDownload.split("/")
fileName = fileName[fileName.length - 1]
var downloadLink = document.createElement('a');
console.log(downloadLink);
downloadLink.href = _OBJECT_URL;
downloadLink.download = fileName;
// document.body.appendChild(downloadLink);
downloadLink.click();
_t.parents('td').find('.download-button').css('display', 'block');
_t.parents('td').find('.download-progress-container').hide();
_t.parents('td').find('.save-file').click();
setTimeout(function() {
window.URL.revokeObjectURL(_OBJECT_URL);
_t.parents('td').find('.download-button').css('display', 'block');
_t.parents('td').find('.save-file').css('display', 'hide');
}, 60 * 1000);
}
});
request.addEventListener('progress', function(e) {
var percent_complete = (e.loaded / e.total) * 100;
_t.parents('td').find('.download-progress').css('width', percent_complete + '%');
});
request.responseType = 'blob';
request.open('get', fileDownload);
request.send();
return false;
});
.demo-container {
width: 400px;
margin: 60px auto;
}
.download-button {
background-color: white;
color: #2980b9;
border: 2px solid #2980b9;
font-family: inherit;
outline: none;
min-width: 100px;
padding: 10px;
font-size: inherit;
border-radius: 2px;
cursor: pointer;
display: block;
margin: 0 auto;
}
.start-download {
text-align: center;
display: none;
}
.download-progress-container {
border: 1px solid #cccccc;
padding: 4px;
display: none;
height: 20px;
}
.download-progress {
background-color: #2980b9;
display: inline-block;
height: 100%;
}
.save-file {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<table>
<tr>
<td class="name">
First link, large file
</td>
</tr>
<tr>
<br><br><br>
<td class="name">
Second link small file
</td>
</tr>
</table>
Here, I am trying to download the file using Ajax and show progress bar. It works perfectly but one problem.
If the first link file is big, and second link file is small.
Then if someone clicks on the first link and file starts downloading and
then immediately clicks on second link and file downloads because of small size, and first link file is still downloading.
After the first link click file is downloaded, it saves with the file with name of second file.
Reproducing steps:
Click on first link
Click on second link
If second link file downloads first, then the name of first link file is same as of second file link
I think, when I call the function again, the filename variable, it gets overwritten something.
Is it possible to use request header instead of filename?
Basically, what I need is.
A way to prevent the overwrite of variable name, when a function is called two times with different parameters, and the function called first time, takes longer to execute, and function called second time, executes within 1 sec.

just replace
fileDownload = $(this).attr('href');
with
var fileDownload = $(this).attr('href');
so that your variable is not considered global by javascript

Related

Detect and notify when download is complete

Let's say I am having a drive link to download a zip file. When I click on the link, the download starts in the browser. Once the download is completed, I want to send an email to notify the user. Is it Possible. I have a .net application(C#) and a page that shows all the drive links. Once a drive link is clicked and gets downloaded completely, I want to send the mail. Also if the download fails in between, I want to send the failed email. Can I do that?
I have updated the below reference code. Add a proper download url link and try this out.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style type="text/css">
body {
padding: 0;
margin: 0;
}
svg:not(:root) {
display: block;
}
.playable-code {
background-color: #f4f7f8;
border: none;
border-left: 6px solid #558abb;
border-width: medium medium medium 6px;
color: #4d4e53;
height: 100px;
width: 90%;
padding: 10px 10px 0;
}
.playable-canvas {
border: 1px solid #4d4e53;
border-radius: 2px;
}
.playable-buttons {
text-align: right;
width: 90%;
padding: 5px 10px 5px 26px;
}
</style>
<style type="text/css">
.event-log {
width: 25rem;
height: 4rem;
border: 1px solid black;
margin: .5rem;
padding: .2rem;
}
input {
width: 11rem;
margin: .5rem;
}
</style>
<title>XMLHttpRequest: progress event - Live_example - code sample</title>
</head>
<body>
<div class="controls">
<input class="xhr success" type="button" name="xhr" value="Click to start XHR (success)" />
<input class="xhr error" type="button" name="xhr" value="Click to start XHR (error)" />
<input class="xhr abort" type="button" name="xhr" value="Click to start XHR (abort)" />
</div>
<textarea readonly class="event-log"></textarea>
<script>
const xhrButtonSuccess = document.querySelector('.xhr.success');
const xhrButtonError = document.querySelector('.xhr.error');
const xhrButtonAbort = document.querySelector('.xhr.abort');
const log = document.querySelector('.event-log');
function handleEvent(e) {
if (e.type=='progress')
{log.textContent = log.textContent + `${e.type}: ${e.loaded} bytes transferred Received ${event.loaded} of ${event.total}\n`;
}
else if (e.type=='loadstart')
{
log.textContent = log.textContent + `${e.type}: started\n`;
}
else if (e.type=='error')
{
log.textContent = log.textContent + `${e.type}: error\n`;
}
else if (e.type=='loadend')
{
log.textContent = log.textContent + `${e.type}: completed\n`;
}
}
function addListeners(xhr) {
xhr.addEventListener('loadstart', handleEvent);
xhr.addEventListener('load', handleEvent);
xhr.addEventListener('loadend', handleEvent);
xhr.addEventListener('progress', handleEvent);
xhr.addEventListener('error', handleEvent);
xhr.addEventListener('abort', handleEvent);
}
function runXHR(url) {
log.textContent = '';
const xhr = new XMLHttpRequest();
var request = new XMLHttpRequest();
addListeners(request);
request.open('GET', url, true);
request.responseType = 'blob';
request.onload = function (e) {
var data = request.response;
var blobUrl = window.URL.createObjectURL(data);
var downloadLink = document.createElement('a');
downloadLink.href = blobUrl;
downloadLink.download ='download.zip';
downloadLink.click();
};
request.send();
return request
}
xhrButtonSuccess.addEventListener('click', () => {
runXHR('https://abbbbbc.com/download.zip');
});
xhrButtonError.addEventListener('click', () => {
runXHR('http://i-dont-exist');
});
xhrButtonAbort.addEventListener('click', () => {
runXHR('https://raw.githubusercontent.com/mdn/content/main/files/en-us/_wikihistory.json').abort();
});
</script>
</body>
</html>
The download link works for zip files as well.
Reference:https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/progress_event#live_example

How to save an Arduino code ".ino extension" from my webpage?

I have created a webpage to write an Arduino code and after that i would like to save the code on the pc, so i use new Blob to save the file and the extension is .ino,, but when i go to the created file i got these messages
Could not create the sketch
Failed to open sketch: 'direction for the file'
I would like to know How to save .ino file
var textToSaveAsBlob = new Blob([encodeURIComponent(arduinoSource)], {type:'data:text/ino;charset=utf-8,'});
var datenow = Date.now();
var fileNameToSaveAs = "arduino_code" +datenow+ ".ino";
saveAs(textToSaveAsBlob, fileNameToSaveAs);
You can use data URIs inside anchor element to generate your download. Browser support varies though, check out Wikipedia description.
For example, the following function takes in your filename and code and generates a download. The way it works is that it creates an anchor element in your DOM, defines charset and content type of your file, appends your code, launches a click event on the anchor element to generate download and then destroys the element.
function download(filename, code) {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(code));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
I have written the following snippet to generate your required functionality, added a bit of Arduino style CSS as well. Hope it helps!
function download(filename, code) {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(code));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
var dateNow = Date.now();
var fileName = 'ArduinoCode';
document.getElementById("arduino-file-name").innerHTML = fileName;
document.getElementById("download-code").addEventListener("click", function() {
var arduinoCode = document.getElementById("arduino-code").value;
var fileNameFull = fileName + "-" + dateNow + ".ino";
download(fileNameFull, arduinoCode);
}, false);
.arduino-menu-bar {
background-color: #006468;
padding: 10px;
}
.arduino-menu-bar--footer {
display: block;
height: 10px;
background-color: #4db7bb;
}
#download-code {
border: none;
color: #4db7bb;
font-size: 30px;
cursor: pointer;
}
#download-code:hover {
color: #7ac4c7;
}
.arduino-files-bar {
background-color: #4db7bb;
padding: 5px 5px 0 5px;
}
.open-file-title {
background-color: #fff;
padding: 5px 15px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
display: inline-block;
color: #006468;
font-family: Arial, Helvetica, sans-serif;
}
#arduino-code {
font-family: 'Fira Code', monospace;
outline: none;
display: block;
width: 100%;
height: 250px;
border: 0px;
font-size: 14px;
resize: none;
}
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css">
<link href="https://fonts.googleapis.com/css?family=Fira+Code&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css">
<div class="arduino-code-container">
<div class="arduino-menu-bar">
<i id="download-code" class="fas fa-cloud-download-alt"></i>
</div>
<div class="arduino-files-bar">
<div id="arduino-file-name" class="open-file-title"></div>
</div>
<textarea id="arduino-code">
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
</textarea>
<div class="arduino-menu-bar arduino-menu-bar--footer"></div>
</div>

How to load multiple file images as data url and change them individually afterwards

On an HTML page, there are two images set using img element. I want to change each of the image by browsing the pc directory. There is no database. After an image is changed, if the page reloads, the old image will be shown.
i.e. there are input options under each image. If I want to change 2nd image, then I will click 'choose file' option under that 2nd image which will open the window for selecting a single image.
I got a piece of code.
<input type='file' accept='image/*' onchange='openFile(event)'>
<img id='output' width=20>
<input type='file' accept='image/*' onchange='openFile(event)'>
<img id='output' width=20>
<input type='file' accept='image/*' onchange='openFile(event)'>
<img id='output' width=20>
<script>
var openFile = function(event) {
var input = event.target;
var reader = new FileReader();
reader.onload = function(){
var dataURL = reader.result;
var output = document.getElementById('output');
output.src = dataURL;
};
reader.readAsDataURL(input.files[0]);
};
</script>
Using this code, I could upload image for only one img element. But if I want to use it for two img elements, it doesn't work if I change id 'output' to class 'output'. I need to uderstand the reason of it and possible solutions for multiple number of images.
https://www.javascripture.com/FileReader
The code below shows:
How to load multiple data urls with one file input and create an image box (image + file input + delete button) for each.
How to individually change the loaded image for each image box.
How to remove completely an image box.
Snippet instructions:
Click the large blue '+' block to add one or more images to the list.
Click an image to change it individually.
Click the red cross in the top right corner of an image to remove it.
It's all file inputs under the hood (change the hidden class to reveal them), they're just hidden because it looks better.
var container = document.getElementById('img_container');
var placeholder = document.getElementById('placeholder');
// utility function doing both createElement and setAttributes
function create(elementName, attributes) {
var elem = document.createElement(elementName);
if (typeof attributes === 'object') {
Object.keys(attributes).forEach(function(attributeName) {
elem.setAttribute(attributeName, attributes[attributeName]);
});
}
return elem;
}
// load a file image as a data url and callback with this data url
function loadImage(file, callback) {
var reader = new FileReader();
reader.onload = function(){
var dataURL = this.result;
callback(dataURL);
};
reader.readAsDataURL(file);
}
// self explainatory
function createAndInsertNewImageBlock(id, dataURL) {
var output = create('div', { 'class': 'img_block' });
// image label, linked to the file input through their for/id attributes
var label = create('label', { 'for': id, 'class': 'img_label' });
var img = create('img', { 'class': 'image', src: dataURL });
label.appendChild(img);
output.appendChild(label);
// single file input triggered by the image label, it is hidden
var input = create(
'input',
{
'type': 'file',
'class': 'hidden',
'accept': 'image/*',
id: id
}
);
// load single data url on change and change the image src
input.addEventListener('change', function() {
loadImage(this.files.item(0), function(data) {
img.src = data;
});
});
output.appendChild(input);
// delete block button
var cross = create('div', { 'class': 'cross' });
cross.addEventListener('click', function() {
output.remove();
});
output.appendChild(cross);
// insert new image block just before the '+' placeholder
container.insertBefore(output, placeholder);
}
// handler for the onChange event of the placeholder's file input
function openFiles(evt) {
var files = evt.target.files;
for (let i = 0; i < files.length; i++) {
var file = files.item(i);
loadImage(file, function(dataURL){
var count = container.children.length;
// lame unique id generation for linking label to input
var id = 'img(' + count + '/' + (Date.now()).toString(16) + ')' + file.name;
createAndInsertNewImageBlock(id, dataURL);
});
};
};
#img_container {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.image {
width: 150px;
margin: 5px;
}
.hidden {
position: absolute;
display: none;
left: -9999px;
}
.img_block {
position: relative;
}
.img_label {
display: block;
cursor: pointer;
}
.plus {
width: 100px;
height: 100px;
font-size: 50px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
color: #2060FF;
}
.plus:after {
content: '+';
}
.cross {
width: 15px;
height: 15px;
font-size: 20px;
font-weight: bold;
background-color: rgba(255,255,255,0.3);
display: flex;
align-items: center;
justify-content: center;
color: #FF2060;
position: absolute;
top: 5px;
right: 5px;
cursor: pointer;
}
.cross:hover {
background-color: rgba(255,255,255,0.6);
}
.cross:after {
content: 'x';
}
<div id="img_container">
<div id="placeholder">
<label class="img_label" for="placeholder_input">
<div class="plus"></div>
</label>
<input type='file' id="placeholder_input" class="hidden" accept="image/*" onchange='openFiles(event)' multiple>
</div>
</div>

Javascript modal window requires scroll down to see

Am currently working with the Soundcloud API to pull images and track links for songs. Am currently showing track info through a modal window upon clicking the album image. However, for the songs on the bottom of the screen, the modal window only appears at the top of the screen, requiring a user to scroll up to see the track info. Probably has something to do with the css positioning but removing position:absolute only puts the modal window at the bottom of all the album images, requiring a scroll down. How can I make it so that a user's click on an image will open and start the modal window right where they currently are, without scrolling? Javascript / jquery /css answers all welcomed.
My CSS:
#modal {
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.2);
position: absolute;
top: 0;
left: 0;
}
#trackinfo {
width:380px;
height: 180px;
padding: 20px;
margin-right: auto;
margin-left: auto;
margin-top: 100px;
box-shadow: 10px 10px 5px;
border-radius: 15px;
text-align: center;
background: #c4e5c1;
font-family: Rockwell, "Courier Bold", Courier, Georgia, Times, "Times New Roman", serif;
}
.hidden {
display:none;
}
My Javascript for show and hide modal:
function doSearch() {
var searchTerm = document.getElementById('search').value;
// Search soundcloud for artists
SC.get('/tracks', { q: searchTerm}, function(tracks) {
for(track in tracks) {
var img = document.createElement('img');
img.setAttribute("src", (tracks[track]["artwork_url"]));
var title = tracks[track].title.replace("'", "\\\'").replace("\"", "\\\"");
linky = document.createElement('a');
linky.setAttribute("href", (tracks[track].permalink_url));
console.log(linky);
img.setAttribute("onclick", "showTrackInfo('" + title + "\\n"+ tracks[track].uri + " " + "\\n\\n(click to close) " + "')");
console.log(img);
if (tracks[track]["artwork_url"] == null) {
console.log(""); }
else {
var Catalog = document.getElementById('catalog');
Catalog.appendChild(img);
$('div#catalog').append('<img src="http://i.imgur.com/rGdvfl7.png">');
}
}
});
};
function showTrackInfo(track) {
var trackInfoElement = document.getElementById("trackinfo");
trackInfoElement.appendChild(document.createTextNode(track));
var modal = document.getElementById("modal");
modal.setAttribute("class", "");
}
function hidemodal() {
var trackInfoElement = document.getElementById("trackinfo");
trackInfoElement.childNodes;
while ( trackInfoElement.firstChild ) trackInfoElement.removeChild( trackInfoElement.firstChild );
var modal = document.getElementById("modal");
modal.setAttribute("class", "hidden");
}
The functions all work well, I just need to know how to position the modal box upon click so that the user doesn't need to scroll to see the trackinfo. Any help much appreciated.
Try position: fixed; on the dialog box, should make it fill the page no matter where they are on it

Upload file styled button with preview feature in IE

I have styled filed upload button and added preview image before upload... basically everything works as a charm in all browsers except IE...
Now to bring you my idea closer it looks like:
http://postimage.org/gallery/22cvzh2g/bcd61d61/
Is there a reason why image isn't showing in IE? I tried in IE9, but I just get the path, while the $('#background-preview').removeClass('hidden'); seems not to be working as it's not removing class hidden...
...also in IE and Opera as file path you will note C:/fakepath/etc... while in FireFox, Chrome and normal browsers it displays just file name. Any help is highly appreciated!
Now in header I have:
<script>
function clearFileInput() {
var oldInput = document.getElementById("upload-bg");
var newInput = document.createElement("input");
newInput.type = "file";
newInput.id = oldInput.id;
newInput.name = oldInput.name;
newInput.onchange = oldInput.onchange;
newInput.className = oldInput.className;
newInput.style.cssText = oldInput.style.cssText;
// copy any other relevant attributes
oldInput.parentNode.replaceChild(newInput, oldInput);
$('#background-preview').addClass('hidden');
var oldInput1 = document.getElementById("FileField");
var newInput2 = document.createElement("input");
newInput2.type = "text";
newInput2.id = oldInput1.id;
newInput2.className = oldInput1.className;
newInput2.style.cssText = oldInput1.style.cssText;
oldInput1.parentNode.replaceChild(newInput2, oldInput1);
}
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#background-image')
.attr('src', e.target.result)
.width(250)
.height(170);
$('#background-preview').removeClass('hidden');
};
reader.readAsDataURL(input.files[0]);
}
}
In body section where actual button is:
<div id="FileUpload">
<input id="upload-bg" type='file' onchange="readURL(this);getElementById('FileField').value = getElementById('upload-bg').value;" />
<div id="BrowserVisible"><input type="text" id="FileField" /></div>
</div>
<div id="background-preview" class="hidden"><img id="background-image" src="#" alt="Bad Image File !" /> </div>
And the CSS that takes care for the customizing file input is:
#FileUpload {
position:relative;
height: 50px;
}
#BrowserVisible {
margin: 5px 0px 5px 0px;
position: absolute;
top: 0px;
left: 0px;
z-index: 1;
background:url(images/button-browse.png) 100% 0px no-repeat;
height:42px;
width:290px;
}
#FileField {
border: 1px solid #BDBDBD;
font-size: 13px;
height: 40px;
margin: 0;
padding: 0;
width: 215px;
}
#upload-bg {
position:relative;
width:290px;
height:43px;
text-align: right;
-moz-opacity:0;
filter:alpha(opacity: 0);
opacity: 0;
z-index: 2;
}
#clear-bg-upload {
position: absolute;
top: 0px;
right: 0px;
width: 16px;
height: 16px;
background: url(images/icon-delete-input.png) top center no-repeat;
}
#background-preview {
border: solid 1px #ccc;
padding: 5px;
position: relative;
display: inline-block;
border-radius: 5px;
-o-border-radius: 5px;
-icab-border-radius: 5px;
-khtml-border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
}
#background-preview.hidden {
display: none;
}
#background-preview img {
margin: 0px auto;
display: block;
max-height: 140px;
max-width: 180px;
width: auto;
}
----------------------------- EDITED -------------------------------
Ok, I went to different approach via Ajax (using) upload and all is wonderful... I just can't figure how to send field value only. Right now it's sent like form, but is there a way to trigger send only the field. Right now it's warped in #FileUploadForm, but I want to use this within a form and since forms can't be nested... I am kind of stuck... except having two forms like I have now, but I would like that file upload filed to be sent like it is now, just without having to wrap it in it's own form.
This is script I am using:
function showRequest(formData, jqForm, options) {
var fileToUploadValue = $('#fileToUpload').fieldValue();
if (!fileToUploadValue[0]) {
$('#result').html('Please select a file.');
return false;
}
$("#loading").show();
return true;
}
function showResponse(data, statusText) {
$("#loading").hide();
if (statusText == 'success') {
var msg = data.error.replace("##", "<br />");
if (data.img != '') {
$('#result').removeClass('hiddenmessage');
$('#result').html('<img src="uploads/thumbs/' + data.img + '" /> ');
// $('#message').html('Click here');
// $('#FileUploadForm').html('');
} else {
$('#result').removeClass('hiddenmessage');
$('#result').html(msg);
}
} else {
$('#result').removeClass('hiddenmessage');
$('#result').html('Unknown error!');
}
}
function StartFileUpload() {
$('#message').html('');
$('#FileUploadForm').ajaxSubmit({
beforeSubmit: showRequest,
success: showResponse,
url: 'upload.php',
dataType: 'json'
});
return false;
}
$('#fileToUpload').live('change', function () {
StartFileUpload();
});
Let's start with paths — in HTML/JS, you can't get full path to attached file due to security concerns. All you can get is file name.
Usually the best approach is to upload file to server with JavaScript when user selects file and then grab preview from that server. You could provide some "delete" button which would enable users to remove pics they uploaded by mistake.
This would enforce deep changes in your application. I recommend File Uploader plugin. Writing your own solution from scratch will be very painful because it requires many hacks for different browsers.

Categories