Visable download to disc starts after download to browser finishes - javascript

I'm currently trying to download files from my mongo DB.
Once I start the download I can see that in the network tab of chrome dev tools the file is downloading. Once it finishes here it's downloaded to the local drive. I really don't know how to skip this and download directly to the local drive.
This is quit bad for the user experience due to the fact, that the files are quite big and it seems like nothing happens.
Server Side:
app.get('/download/single',function(req,res){
gfs.findOne({ _id: req.query.targetFile}, function(err,file){
if (err) {
return res.status(400).send(err);
}
else if (!file) {
return res.status(404).send('Error on the database looking for the file.');
}
else{
res.set('Content-Type', "application/vnd.android.package-archive")
var readstream = gfs.createReadStream({
_id: req.query.targetFile,
})
readstream.pipe(res)
readstream.on('end',function(){
res.end()
})
}
})
})
Client Side:
app.service('Download',function($http){
this.single = function(id){
return $http({
method: 'GET',
url: '/download/single',
params: {
targetFile: id
},
transformResponse: [function (data) {
return data;
}]
}).success(function(response){
return response
})
}
})
app.controller('downloadCtrl',function($scope,$routeParams,$window,Download){
Download.single($routeParams.id).success(function(data){
if(data){
var blob = new Blob([data], {
type: "application/vnd.android.package-archive"
})
saveAs(blob,'test.apk',true)
}
})
})

my recommendation is to put the chunk of code to inside some function that is callend on the event of your choice
$scope.showLoader = false;
$scope.onSomeButtonClick = function() {
$scope.showLoader = true;
Download.single($routeParams.id).success(function(data){
$scope.showloader = false;
if(data){
var blob = new Blob([data], {
type: "application/vnd.android.package-archive"
})
saveAs(blob,'test.apk',true)
}
})
}
what is happening in your case, the controller gets initialized, and starts executing its code. what you want to do initialize only the event listeners, that you wish to start the download.
hope this helps. cheers

Related

Blocking execution until user selects an option from the browser's download modal

I'm using window.location.href = some_url in a javascript bit. This brings up a menu from the browser, like so:
This is already happening within a promise - I have other .then() after, but I'd like them to hold on until the user has selected something from that menu.
Any way to access this? Code bits:
$.ajax({
url: url,
type: "POST",
data: formData,
contentType: false,
processData: false
})
.done(function(response, jqXHR){
new Promise(function(resolve, reject){
if (response.download){
console.log("downloading files sent.... ")
window.location.href=response.download.url
// I'd like to block here on the popup window before moving
resolve(response);
} else {
console.log("No downloads.")
resolve(response);
}
return response
})
.then(...)
Okay, so the real answer is you need to not be lazy and use something like a blob instead. Client-side, something like that:
$.ajax({
url: url,
type: "POST",
data: formData,
contentType: false,
processData: false
})
.done(function(response, jqXHR){
console.log("POST success,")
if (response.download.url) {
console.log("content to download")
fetch(response.download.url)
.then(function (resp){
let blob = resp.blob()
const header = resp.headers.get('Content-Disposition');
let filename = header.split(';')[1].split('=')[1];
response["filename"] = filename
return blob
// return resp
})
.then( function(blob) {
// blob = resp.blob()
const blob_url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.style.display = "none"
a.href = blob_url
a.download = response.filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(blob_url)
// alert("download should be finished now")
return response
})
.then(function (response){
if (response.current_page && response.current_page.refresh) {
console.log("Refreshing page...")
location.reload(true);
console.log("Done.")
} else {
console.log("No refresh.")
}
return response
})
.catch((err)=>{console.log(err)} )
Then server-side - I'm using django, but the details would be similar in other frameworks:
logger.info(f"Creating tmp file for pdf response")
with tempfile.NamedTemporaryFile() as file:
file.write(pdf)
file.seek(0)
stream = file.read()
# build the http response
httpresponse = HttpResponse(content_type='application/pdf;', content=stream)
httpresponse['Content-Disposition'] = f'attachment; filename={value["filename"]}'
httpresponse['Content-Transfer-Encoding'] = 'binary'
return httpresponse
This works, because then I'm waiting on the fetch() call to finish before I move on to build the blob() & recover it locally. In other words, the "waiting" is managed by the fetch()' promise, and it only moves on once the server returned the file.
Then I can refresh the page without worrying, since I have the data in the browser's cache at that point. Loosing the state with a reload() at that point is fine.

How to store PDF in mongodb?

I was successful in storing images in mongodb using the base64 url. But when I tried to do so with PDFs, it gave an url which does not work. Wait, let me explain, when I put the image base64 url in the req.body of the POST request, the special signs would get disappeared, so I tried encodeURIComponent() method to make it error free. After that I found that storing the huge string in mongodb was too short to fit in the db, so I tried: app.use(express.json({limit: '50mb'})); app.use(express.urlencoded({limit: '50mb', extended: false })); and It worked! but when the client requests the base64 url, it would come encoded, so I put decodeURIComponent() to decode it and was not a great issue nd I got the desired result, yet with the Image one.
The main issue issue is when it comes to PDF. I don't know why it's happening with PDF only! when I make base64 url in CLIENT side and test it, it works fine, but when it comes to server side, all the mess happens. please help me deal with this.
Note: "I don't want to use Gridfs, formidabe, multer etc for file things"
here's my piece of code:
$('#seasonForm').submit(async function (e) {
e.preventDefault();
const form = $(this);
const ImgFile = document.getElementById('seasonThumbnail').files[0];
const PDFFile = document.getElementById('seasonPDF').files[0];
const imgurl = encodeURIComponent(await getBase64(ImgFile));
const PDFurl = encodeURIComponent(await getBase64(PDFFile));
const url = '/uploadSeason';
$.ajax({
type: "POST",
url: url,
data: form.serialize()+`&Version=<%- NxtSeasons %>&image=${imgurl}&PDF=${PDFurl}`,
success: data => {
console.log(data.message);
if (data.status == "error") {
showIt(".alert", data.message, "error");
} else {
showIt(".alert", data.message, "success");
}
}
});
})
wait, now don't get confused with getBase64() and showIt. these are my functions. getBase64() is a promice which returns base64 url of the file and showIt() is type of alert which I made. Now if you don't know what is base64 url, this is the getBase64 one's code:
const getBase64 = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
My nodejs code:
app.post("/uploadSeason", admin, async (req, res) => {
try {
const { Name, Desctiption, Version, image, PDF } = req.body;
const newSeason = new Season({
Name,
Desctiption,
Version,
image: encodeURIComponent(image),
PDF: encodeURIComponent(PDF),
});
await newSeason.save();
res.json({
status: "success",
message: "Season added successfully"
});
} catch (e) {
console.log(e);
res.json({
status: "error",
message: e
});
}
});

PWA application: issues with caching

I am developing a PWA app. Although I am a beginner in JavaScript and HTML, I was able to almost complete the coding. But I have an issue with caching.
The app is quite simple: in a single page there are some images that are used to display PDF files. When no network is present, it should work locally from the cache. The app will be used on an Android tablet. I ma testing on Windows 10 using Chrome. That was working fine. During the tests, if a PDF file was changed on the server, and then the network was back I needed to empty the cache for the app to show the new PDF file. I modified the design to use the cache then network method, but that does not work. I just can't figure out what's wrong. Here is the actual code.
/sw.js
const staticAssets = [
'/',
'/index.html',
'/sw.js',
'css/bootstrap.min.css',
'js/app.js',
'js/bootstrap.min.js',
'js/jquery.min.js',
'js/jquery.slim.min.js',
'js/popper.min.js',
'/icons/generic_128.png',
'icons/generic_512.png',
'images/Ashkeepers.png',
'images/Canvases.png',
'images/Forfaits thématiques.png',
'images/Funeral urn.png',
'images/logo fictif.png',
'images/Memorial products.png',
'images/Produits commémoratifs.png',
'images/Reliquaires.png',
'images/Thematic packages.png',
'images/Toiles.png',
'images/Urnes.png',
'pdf/02_urnes_funeraires_02_ENG_HR.pdf',
'pdf/02_urnes_funeraires_04_HR.pdf',
'pdf/03_toiles_03_ENG_HR.pdf',
'pdf/03_toiles_04_HR.pdf',
'pdf/04_forfaits_thematiques_05_ENG_HR.pdf',
'pdf/04_forfaits_thematiques_12_HR.pdf',
'pdf/05_reliquaires_03_ENG_HR.pdf',
'pdf/05_reliquaires_04_HR.pdf',
'pdf/06_produits_funeraires_02_ENG_HR.pdf',
'pdf/06_produits_funeraires_03_HR.pdf'
];
self.addEventListener('install', function (event) {
console.log('App Installed');
event.waitUntil(
caches.open('static')
.then(function (cache) {
cache.addAll(staticAssets);
})
);
});
self.addEventListener('activate', function () {
console.log('App Activated');
});
/*
Version originale
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(res) {
if (res) {
console.log("From cache");
return res;
} else {
console.log("From Internet");
return fetch(event.request);
}
})
);
});
*/
/* Cache the network */
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open('static').then(function(cache) {
return fetch(event.request).then(function(response) {
cache.put(event.request, response.clone());
console.log('Data en cache: ' + response.clone()); /* AJOUTÉ */
return response;
});
})
);
});
/js/App.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(function() {
console.log('SW registered');
});
}
/js/feed.js
var networkDataReceived = false;
// fetch fresh data
var networkUpdate = fetch('/data.json').then(function(response) {
return response.json();
}).then(function(data) {
networkDataReceived = true;
updatePage(data);
}).catch(function() {
/* Il faut attraper l'exception si data.json est introuvable */
});
// fetch cached data
caches.match('/data.json')
.then(function(response) {
if (!response) throw Error("No data");
return response.json();
}).then(function(data) {
// don't overwrite newer network data
if (!networkDataReceived) {
updatePage(data);
}
}).catch(function() {
// we didn't get cached data, the network is our last hope:
return networkUpdate;
});
Can someone bring some light to my poor brain ?
Thanks a lot
EDITED EDITED EDITED
I forgot to tell how I display PDF files:
<a href="pdf/03_toiles_03_ENG_HR.pdf" class="mx-auto d-block">
Can that explains the issues I have ? I know that a service worker scope is specific, and I wonder if it could be that the SW don't work on the 'page' that's used to display the PDF.

How to download files on the browser

I'm working on a simple project to download videos from the browser using youtube-dl, for study porpuses.
And I was wondering how to download local files (mp4) on the browser using axios. The browser starts the download, but when it finishes, I can't open the mp4 file.
This is my code Go snippet:
func download(w http.ResponseWriter, r *http.Request) {
fileName := "video.mp4"
data, err := ioutil.ReadFile(fileName)
if err != nil {
log.Print(err)
return
}
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", "attachment; filename="+fileName)
w.Header().Set("Content-Transfer-Encoding", "binary")
w.Header().Set("Expires", "0")
http.ServeContent(w, r, fileName, time.Now(), bytes.NewReader(data))
}
And this is my JS function, triggered when the user input a text:
<script>
import axios from 'axios';
export default {
name: 'govideo',
data() { return {
url: '',
} },
methods: {
postreq() {
axios.post("http://127.0.0.1:8090/download", {
data: this.url,
responseType: 'blob'
}).then((response) => {
var fileURL = window.URL.createObjectURL(new Blob([response.data]));
var fileLink = document.createElement('a');
fileLink.href = fileURL;
fileLink.setAttribute('download', 'video.mp4');
document.body.appendChild(fileLink);
fileLink.click();
})
}
}
}
</script>
There's no problem with the video file, but when I download it from the browser I can't open it.
I'm trying to download the file from my post request, should I do a separate get request for it?
There's something wrong with my code, or I'm missing something?
The problem is the method of handle the response in axios.I hope the code could help you
validateGrpc() {
axios.post("http://127.0.0.1:8090/download", {
data: this.url,
responseType: 'blob'
})
.then(response => {
var blob = new Blob([response.data]);
var downloadElement = document.createElement("a");
var href = window.URL.createObjectURL(blob); //create the download url
downloadElement.href = href;
downloadElement.download = "test.mp4"; //the name of the downloaded file
document.body.appendChild(downloadElement);
downloadElement.click(); //click to file
document.body.removeChild(downloadElement); //remove the element
window.URL.revokeObjectURL(href); //release the object of the blob
// console.log(response);
})
.catch(response => {
console.log(response);
});
},
If you use my code to download the file ,you will see the file from the browser.
I have see your code on the github.I think you should put the video.mp4 into the directory vue-go-study\backend.Then everything goes well.

Problems with File plugin in Ionic2

I'm integrating Quickblox in my Ionic2 app, for now I was able to do all the things except uploading a file.
In Quickblox you have to upload a file using a function made by them that according to js documentation looks like this:
var inputFile = $("input[type=file]")[0].files[0];
var params = {name: inputFile.name,
file: inputFile,
type: inputFile.type,
size: inputFile.size,
public: false};
QB.content.createAndUpload(params, function(err, response){
if (err) {
console.log(err);
} else {
console.log(response);
var uploadedFile = response;
var uploadedFileId = response.id;
}
});
So I translated above code to typescript and I have something like this:
uploadFile(filename) {
File.resolveDirectoryUrl(cordova.file.dataDirectory).then(
(dirEntry) => {
File.getFile(dirEntry, filename, { create: false }).then(
(fileEntry) => {
console.log(fileEntry);
fileEntry.file((file) => {
console.log(file);
var params = {
name: file['name'],
file: file,
type: file['type'],
size: file['size'],
'public': false
};
quickblox.content.createAndUpload(params,
(err, response) => {
if (err) {
console.log(err);
} else {
console.log(response);
var uploadedFileId = response.id;
var msg = {
type: 'groupchat',
body: "[attachment]",
extension: {
save_to_history: 1,
}
};
msg["extension"]["attachments"] = [{ id: uploadedFileId, type: 'photo' }];
quickblox.chat.send(this.xmpp_room_jid, msg);
}
});
})
}).catch(
(err) => {
console.log(err);
}
);
}
);
}
This work in the terms of "I get ok responses from quickblox server", but when I go to the admin console of quickblox to check the uploaded content I find out that the image I uploaded has 0 bytes.
So after checking the code for a while I compared side by side all my function calls with the example app of quickblox and the only difference I could find was in the File object.
This is the File object I get in my Ionic 2 app:
And this is the one I get in the quickblox js example:
All the others things looks identically except this File object.
I'm almost sure that this is the problem I'm having, and because I'm very newbie in this field, I couldn't find a way to cast from my File object in Ionic to something like the File object in the js example.
Thanks in advance at all for your time and help.
EDIT:
I attach the requests/responses logs from my Ionic app:
Could you please post the code you used to connect to chat, create a session, open a video call?
The documentation on quickblox is very bad and i got stuck at connecting to chat.

Categories

Resources