Code is executing multiple times per event: Multiple downloads - javascript

I want to download a JSON object from within the content script. At first, when I request the download, it downloads one file, but at the second request, it downloads two files; at the third request, three files are downloaded, etc.
Background.js
chrome.browserAction.onClicked.addListener(function (tab) {
download();
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
//Alert the message
console.log(request);
chrome.downloads.download({
url: request.method,
filename: request.name
}, function (downloadId) {
});
//You have to choose which part of the response you want to display
// ie. request.method
//alert('The message from the content script: ' + request.method);
//Construct & send a response
sendResponse({
response: "Message received"
});
});
});
function download() {
chrome.tabs.executeScript(null, {
file: "jquery.js"
}, function () {
chrome.tabs.executeScript(null, {
file: "content_script.js"
});
});
}
Content Script
function sendMessage(url, filename) {
//Construct & send message
chrome.runtime.sendMessage({
method: url,
name: filename
}, function (response) {
//Alert the message
//You have to choose which part of the response you want to display
// ie. response.response
//alert("The response from the background page: " + response.response);
});
}
var json = JSON.stringify(ticket);
var blob = new Blob([json], {
type: "application/json"
});
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.download = "backup.json";
a.href = url;
a.textContent = "Download backup.json";
var container = document.getElementById('ticketDetail');
//container.appendChild(a);
var fName = ticket.date.replace(".", "_")
sendMessage(url, fName.replace(".", "_") + ".json");

As is usually the case with this pattern of problem, the issue is that you are adding multiple anonymous listeners to an event. Specifically, you are adding yet another chrome.runtime.onMessage listener each time the action_button is clicked. You need to add the listener only once.
The simple solution to this is to just add the chrome.runtime.onMessage once:
chrome.browserAction.onClicked.addListener(function (tab) {
download();
});
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
//Alert the message
console.log(request);
chrome.downloads.download({
url: request.method,
filename: request.name
}, function (downloadId) {
});
//You have to choose which part of the response you want to display
// ie. request.method
//alert('The message from the content script: ' + request.method);
//Construct & send a response
sendResponse({
response: "Message received"
});
});
function download() {
chrome.tabs.executeScript(null, {
file: "jquery.js"
}, function () {
chrome.tabs.executeScript(null, {
file: "content_script.js"
});
});
}

Related

Reducing Duplicate Javascript Code for Ajax Request

I'm writing this question because I have an AJAX request for deleting posts into my website that is working fine but I have duplicated it multiple times to match the URL for different Custom Post Type that I have in the page.
Here the original code:
jQuery(".delete-listing-btn").on("click", function(e) {
var thisPost = jQuery(e.target).parents(".agent-dashboard-listing-card")
jQuery.ajax({
beforeSend: (xhr) => {
xhr.setRequestHeader('X-WP-Nonce', msSiteData.nonce);
},
url: 'https://mallorca-select.com/wp-json/wp/v2/properties-for-sale/' + thisPost.data('id'),
type: 'DELETE',
success: function () {
alert(" Listing Deleted Successfully! ");
},
error: function (request, error) {
console.log(arguments);
alert(" Can't do because: " + error);
}
});
});
In these functions the only thing that changes is a part of the URL request like this:
'https://example.com/wp-json/wp/v2/post-type-1/' + thisPost.data('id')
'https://example.com/wp-json/wp/v2/post-type-2/' + thisPost.data('id')
'https://example.com/wp-json/wp/v2/post-type-3/' + thisPost.data('id')
'https://example.com/wp-json/wp/v2/post-type-4/' + thisPost.data('id')
I'm wondering if there is a method for detecting the post type of the post where the delete button is clicked and inject inside the URL request so I don't have to duplicate it 4 times only to change the custom post type inside the url.
Move the common AJAX code to a separate function and pass the specific URL you need in each case.
jQuery(".delete-listing-btn").on("click", function(e) {
var thisPost = jQuery(e.target).parents(".agent-dashboard-listing-card")
sendRequest('properties-for-sale');
});
const sendRequest = (requestUrl) => {
jQuery.ajax({
beforeSend: (xhr) => {
xhr.setRequestHeader('X-WP-Nonce', msSiteData.nonce);
},
url: `https://mallorca-select.com/wp-json/wp/v2/${requestUrl}/${thisPost.data('id')}`,
type: 'DELETE',
success: function () {
alert(" Listing Deleted Successfully! ");
},
error: function (request, error) {
console.log(arguments);
alert(" Can't do because: " + error);
}
});
};
To do what you require you can use another data attribute to hold the post type URL route - as you already are for the id:
jQuery($ => {
$(".delete-listing-btn").on("click", e => {
let $button = $(e.target);
let $thisPost = $button.parents(".agent-dashboard-listing-card");
let url = `https://example.com/wp-json/wp/v2/${$button.data('post-type')}/${$button.data('id')}`;
console.log(url);
// your AJAX request here...
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<button class="delete-listing-btn" data-post-type="post-type-1" data-id="1">Post Type 1</button>
<button class="delete-listing-btn" data-post-type="post-type-2" data-id="2">Post Type 2</button>
<button class="delete-listing-btn" data-post-type="post-type-3" data-id="3">Post Type 3</button>
<button class="delete-listing-btn" data-post-type="post-type-4" data-id="4">Post Type 4</button>

Javascript Callback function after download Excel

I need to send mail to user after downloading .csv file. I am not sure how to set callback function after file gets downloaded.
$.ajax({
url: '#Url.Action("GetAllCustomer", "Customers")',
type: 'POST',
data: { 'customerIds': strCustomerId },
success: function (result) {
if (result == "Success") {
location.href = '#Url.Action("DownloadFile", "Customers", new { extension = "csv"})';
} else {
toastLast = toastr.error("No data found", "Generating File");
}
}
});
In above code in first call i am getting all the customers. On success callback i am calling DownloadFile method to download csv file. i have requirement to send mail after downloading file but i am not sure how will i know that file is downloaded. Or Can I achieve with some other way.
Please find my DownloadFile method of controller as below.
public ActionResult DownloadFile(string extension)
{
var dateTime = DateTime.Now.ToString("M.dd.yy");
var fileName = dateTime + " Application File." + extension;
var array = TempData["Output"] as byte[];
if (array != null)
{
var file = File(array, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
return file;
}
else
{
return new EmptyResult();
}
}
Don't use this line
location.href = '#Url.Action("DownloadFile", "Customers", new { extension = "csv"})';
Instead use a ajax request to the Action method
$.ajax({
type: "POST",
url: '#Url.Action("DownloadFile", "Customers")',
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(){
//add your sccuess handlings
}
error: function(){
//handle errors
}
});

Ask user to open or download file

I have a C# method that exports a database table and a JS function that starts a download. The JS is this:
function Export()
{
$.ajax({
cache: false,
type: "POST",
url: '#Url.Action("exportDatabaseTable", "MyController")',
data:
{
'type': "xls"
},
dataType: "text",
beforeSend: function () //Display a waiting message
{
$('#wait').show();
},
success: function (result) //Result is "Message|FilePath"
{
var res = result.split('|');
if(res[0]!="Nothing found")
window.location = "DownloadDatabaseTable?fileName=" + res[1];
alert(res[0]);
},
error: function ()
{
alert("Export failed");
},
complete: function () //Hide the waiting message
{
$('#wait').hide();
}
});
}
While the C# one, called in the success function, is this:
[HttpGet]
public ActionResult DownloadDatabaseTable(string fileName)
{
System.IO.FileInfo file = new System.IO.FileInfo(fileName);
byte[] fileBytes = System.IO.File.ReadAllBytes(fileName);
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, System.IO.Path.GetFileName(fileName));
}
Everything works, but the download starts automatically. Is there a way to show the download windows and let the user choose to download (and where to download it) or to open the file?

How do you upload a single image with Dropzone.js?

Dropzone.js seems to be uploading images as multi-part form data. How do I get it to upload images in the same way an image upload would work with cURL or a binary image upload with Postman?
I'm getting a pre-signed URL for S3 from a server. The pre-singed URL allows an image upload, but not form fields:
var myDropzone = new Dropzone("#photo-dropzone");
myDropzone.options.autoProcessQueue = false;
myDropzone.options.autoDiscover = false;
myDropzone.options.method = "PUT";
myDropzone.on("addedfile", function ( file) {
console.log("Photo dropped: " + file.name );
console.log("Do presign URL: " + doPresignUrl);
$.post( doPresignUrl, { photoName: file.name, description: "Image of something" })
.done(function( data ) {
myDropzone.options.url = data.url
console.log(data.url);
myDropzone.processQueue();
});
});
If I use the returned URL with Postman and set the body to binary and attach the image, then the upload works fine. However, if the Dropzone library uses the same URL to upload the image to S3 then I get a 403 because S3 does not expect form fields.
Update:
An Ajax alternative works as below with a S3 signed url, but Dropzone.js does not seem willing to put the raw image data in the PUT message body.
$.ajax( {
url: data.url,
type: 'PUT',
data: file,
processData: false,
contentType: false,
headers: {'Content-Type': 'multipart/form-data'},
success: function(){
console.log( "File was uploaded" );
}
});
Set maxFiles to 1.
Dropzone.autoDiscover = false;
dzAllocationFiles = new Dropzone("div#file-container", {
url: "api.php?do=uploadFiles"
, autoDiscover: false
, maxFiles: 1
, autoQueue: true
, addRemoveLinks: true
, acceptedFiles: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
});
dzAllocationFiles.on("success", function (file, response) {
// Success Operations
});
dzAllocationFiles.on("maxfilesexceeded", function (file, response) {
allocationFileNames = [];
this.removeAllFiles();
this.addFile(file);
});
Add below options, then working.
myDropzone.options.sending = function(file, xhr) {
var _send = xhr.send;
xhr.send = function() {
_send.call(xhr, file);
}
}

Web API: Upload files via Ajax post

I have this following snippet for uploading files via Ajax post (using Knockout js library)
ko.bindingHandlers.fileUpload = {
init: function (element, valueAccessor) {
$(element).after('<div class="progress"><div class="bar"></div><div class="percent">0%</div></div><div class="progressError"></div>');
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var options = ko.utils.unwrapObservable(valueAccessor()),
property = ko.utils.unwrapObservable(options.property),
url = ko.utils.unwrapObservable(options.url);
if (property && url) {
$(element).change(function () {
if (element.files.length) {
var $this = $(this),
fileName = $this.val();
alert(fileName);
// this uses jquery.form.js plugin
$(element.form).ajaxSubmit({
url: url, //WEB API URL
type: "POST",
dataType: "text", //I want to upload .doc / excell files
headers: { "Content-Disposition": "attachment; filename=" + fileName },
beforeSubmit: function () {
$(".progress").show();
$(".progressError").hide();
$(".bar").width("0%")
$(".percent").html("0%");
},
uploadProgress: function (event, position, total, percentComplete) {
var percentVal = percentComplete + "%";
$(".bar").width(percentVal)
$(".percent").html(percentVal);
},
success: function (data) {
$(".progress").hide();
$(".progressError").hide();
// set viewModel property to filename
bindingContext.$data[property](data);
},
error: function (jqXHR, textStatus, errorThrown) {
$(".progress").hide();
$("div.progressError").html(jqXHR.responseText);
}
});
}
});
}
}
}
now my problem is creating the WEB API for this.
$(element.form).ajaxSubmit({
url: url, //WEB API URL
type: "POST",
dataType: "text", //I want to upload .doc / excell files
headers: { "Content-Disposition": "attachment; filename=" + fileName },
I want to upload Word/Excell document. Can someone give me an idea how to get this done in ASP.NET WEB API?
UPDATE:
I finally made an WEB API for this
public class UploadController : ApiController
{
public async Task<HttpResponseMessage> PostFormData()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
try
{
// Read the form data.
await Request.Content.ReadAsMultipartAsync(provider);
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
Trace.WriteLine(file.Headers.ContentDisposition.FileName);
Trace.WriteLine("Server file path: " + file.LocalFileName);
}
return Request.CreateResponse(HttpStatusCode.OK);
//return new HttpResponseMessage()
//{
// Content = new StringContent("File uploaded.")
//};
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
It works ok (data successfully uploaded) but the problem is, it seems like in doesnt hit this section after the data is succesfully uploaded.
success: function (data) {
$(".progress").hide();
$(".progressError").hide();
// set viewModel property to filename
bindingContext.$data[property](data);
},
I have to inform the client of what the status of the upload.

Categories

Resources