I have one demo-file.csv file and it is in the assets/csv folder, so how can I download it from mobile,
here is my HTML & COMPONENT code.
HTML CODE
<button ion-button type="button" block (click)="downloadFile('assets/csv/demo-file.csv', 'demo-file.csv')">Download Demo File</button>
COMPONENT CODE
public downloadFile(link: any, fileName: any) {
if (link) {
let path = null;
this.showWaitingLoading();
if (this.platform.is('ios')) {
path = this.file.documentsDirectory;
} else {
path = this.file.dataDirectory;
}
const transfer = this.transfer.create();
transfer.download(link, path + fileName).then(entry => {
this.dismissWaitingLoading();
this.openFile(entry.toURL());
}).catch(() => {
this.dismissWaitingLoading();
this.showToastMsg('error', "Something went wrong");
});
}
}
/* ================= OPNE FILE FUNCTION ===========*/
public openFile(path: any) {
this.fileOpener.open(path, 'application/*')
.then(() => console.log('File is opened'))
.catch((e: any) => console.log('Error openening file', e));
}
I'm not able to download the file, is there any thing missing in my PATH?
Try to read it using Http get and write it as a Blob, Sample code as follows,
export class csvPage {
csvData: any[] = [];
headerRow: any[] = [];
constructor(public navCtrl: NavController,
public navParams: NavParams,
private http: Http) {
this.readCsvData();
}
private readCsvData() {
this.http.get('assets/dummyData.csv')
.subscribe(
data => this.extractData(data),
err => this.handleError(err)
);
}
private extractData(res) {
let csvData = res['_body'] || '';
let parsedData = papa.parse(csvData).data;
this.headerRow = parsedData[0];
parsedData.splice(0, 1);
this.csvData = parsedData;
}
downloadCSV() {
let csv = papa.unparse({
fields: this.headerRow,
data: this.csvData
});
// Dummy implementation for Desktop download purpose
var blob = new Blob([csv]);
var a = window.document.createElement("a");
a.href = window.URL.createObjectURL(blob);
a.download = "newdata.csv";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
private handleError(err) {
console.log('something went wrong: ', err);
}
}
Html Code
<button ion-button type="button" block (click)="downloadFile('demo-file.csv')">Download Demo File</button>
Component Code
declare var cordova: any; // declare Top Section of component
public downloadFile(link: any) {
if (link) {
let path = null;
this.showWaitingLoading();
if (this.platform.is('ios')) {
path = cordova.file.documentsDirectory;
} else {
path = cordova.file.dataDirectory;
}
const transfer = this.transfer.create();
const imageLocation = `${cordova.file.applicationDirectory}www/assets/csv/${link}`;
transfer.download(imageLocation, path + link).then(entry => {
this.dismissWaitingLoading();
this.openFile(entry.toURL());
}).catch(() => {
this.dismissWaitingLoading();
this.showToastMsg('error', "Something went wrong");
})
}
/* ================= OPNE FILE FUNCTION ===========*/
public openFile(path: any) {
this.fileOpener.open(path, 'application/*')
.then(() => console.log('File is opened'))
.catch((e: any) => console.log('Error openening file', e));
}
Please try this one
You can use a library... Also, the HttpClient can read data as Blob for you.
npm i file-saver
// my.component.ts
import * as fileSaver from 'file-saver';
export class MyComponent {
constructor(private http: HttpClient){}
downloadFile(path: string) {
this.startLoading();
this.http.get(`${MY_APP_URL}/${path}`, { responseType: 'blob' })
.pipe(tap(blob: Blob => fileSaver.saveAs(blob, 'your_csv_file_name.csv')))
.subscribe(() => this.stopLoading(), err => this.handleErr(err));
}
}
Hope this helps a little :-)
Related
I'm getting R14 memory errors in Heroku that ping consistently. I can't tell if I have a memory leak, or if the code I'm writing just has a large memory footprint (and I'm new to debugging memory leaks). All I've done so far is just refactored global variables to instantiate within each function but I don't know what to try next or debugging methods (eg in the browser or in node) to track things down.
I have a functional nodeJS app for personal use hosted on heroku which is a keystonejs cms.
In the frontend of my app, I have a UI where users can paste in any arbitrary link to a website. In my Heroku app's logic I then use playwright to scrape an image from that URL and upload it to my own cloudinary account for easier/faster rendering in NextJS.
Any time I call this function in heroku, I start getting R14 errors that just keep pinging even after the function has executed.
This is the logic I'm using to try and scrape an image (link to pastebin if thats easier: https://pastebin.com/bTCvdm33)
const cloudinary = require('cloudinary').v2
require('dotenv').config()
const playwright = require('playwright-chromium')
const cheerio = require('cheerio')
const psl = require('psl')
// just maps domains to css selectors ({'example.com': '#product_image0'})
import { vendorHeroImageMap } from './vendorHeroImageMap'
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
})
function doesImageExist(url: string): Promise<boolean> {
return new Promise((resolve) => {
fetch(url, { method: 'HEAD' })
.then((res) => {
if (res.ok) {
console.log('image exists')
return resolve(true)
} else {
console.log('image does not exist')
return resolve(false)
}
})
.catch(() => resolve(false))
})
}
function resolveImage(imageUrl: string, hostname: string) {
try {
const url = new URL(imageUrl, hostname)
console.log('resolved url to ' + url.href)
return url.href
} catch (e) {
console.log("couldn't resolve image url: ", e)
return null
}
}
export default async function scrapeURL(
urlString: string
): Promise<string | undefined> {
console.log('getImageFromURL: ', urlString)
const url = new URL(urlString)
const domain = psl.get(url.hostname)
const browser = await playwright.chromium.launch({ args: ['--no-sandbox'] })
const context = await browser.newContext({
userAgent:
'5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
})
const page = await context.newPage()
await page.goto(url.href)
const pageURL = page.url()
console.log({ pageURL })
const html = await page.content()
const $ = cheerio.load(html)
await page.close()
await browser.close()
let imageURL: string | null = null
console.log(url.hostname)
const amzn = ['amazon.com', 'amazon.ca', 'amazon.co.uk', 'a.co']
if (amzn.includes(domain)) {
console.log('using amazon')
imageURL = $('.imgTagWrapper').find('img').attr('src')
if (!imageURL) {
imageURL = $('img.s-image').attr('src')
}
if (!imageURL) {
imageURL = $('img#imgBlkFront').attr('src')
}
if (!imageURL) {
imageURL = $('img.frontImage').attr('src')
}
}
if (!imageURL) {
imageURL = $('meta[property="og:image"]').attr('content')
if (imageURL) console.log('used og:image property')
}
if (!imageURL) {
imageURL = $('meta[name="og:image"]').attr('content')
if (imageURL) console.log('used og:image name')
}
if (!imageURL) {
imageURL = $('meta[property="twitter:image"]').attr('content')
if (imageURL) console.log('used twitter:image')
}
// try manual mapping
if (!imageURL && domain in vendorHeroImageMap) {
console.log('use manual mapping')
const selector = vendorHeroImageMap[domain].selector
const selectorType = vendorHeroImageMap[domain].selectorType
if (selectorType === 'container') {
console.log('using container selector')
const heroContainer = $(selector)
imageURL = heroContainer.find('img').not('[href*=icon]').attr('src')
} else {
console.log('using direct selector')
console.log({ selector })
imageURL = $(selector).attr('src')
}
if (imageURL) console.log('vendorHeroImageMap: ', imageURL)
}
// all else fails just fetch the first image
if (!imageURL) {
imageURL = $('img:first').attr('src')
if (imageURL) console.log('used img src')
}
// resolve url
// should handle imageURL cases such as:
// "//static.smallable.com/1396969-thickbox/.jpg"
// "/assets/upper-story-og-banner.jpg"
if (imageURL) {
imageURL = resolveImage(imageURL, url.origin)
}
let imageExists = false
if (imageURL) {
imageExists = await doesImageExist(imageURL)
console.log({ imageExists })
}
if (imageURL && imageExists) {
console.log('Successfully fetched: ', imageURL)
return cloudinary.uploader
.upload(imageURL, {
folder: 'my_folder'
quality: 'auto',
transformation: [
{ crop: 'fill', gravity: 'auto', height: 366, width: 650 },
],
timeout: 10000,
})
.then((result: any) => {
console.log('Success: ', result.secure_url)
return result.secure_url
})
.catch((e: any) => {
console.log('error uploading to cloudinary', e)
return imageURL
})
}
console.log('Failed to parse image')
return Promise.resolve(undefined)
}
I'm running a function that uploads an image to Firebase and sets the data to its respective user:
import { Component, OnInit } from '#angular/core';
import { AngularFirestoreDocument } from '#angular/fire/firestore';
import { FbUser } from 'src/app/common/fb-user';
import { AuthService } from 'src/app/services/auth.service';
import { NgxImageCompressService } from 'ngx-image-compress';
#Component({
selector: 'app-profile-info',
templateUrl: './profile-info.component.html',
styleUrls: ['./profile-info.component.scss']
})
export class ProfileInfoComponent implements OnInit {
constructor(
public _auth: AuthService
) { }
ngOnInit(): void {
}
currentImageUrl: string = "";
async uploadPicture(e: any) {
const file = e.target.files[0]
const filePath = this._auth.userData.uid
const task = await this.uploadImage(filePath, file)
if (task) {
//Promise.resolve().then(() => window.location.reload())
this.currentImageUrl = await this._auth._afstg.ref(task).getDownloadURL().toPromise();
const userRef: AngularFirestoreDocument<any> = this._auth._afs.doc(`users/${filePath}`);
const userData: FbUser = {
uid: filePath,
email: this._auth.userData.email,
displayName: this._auth.userData.displayName,
photoURL: this.currentImageUrl,
emailVerified: this._auth.userData.emailVerified
}
return userRef.set(userData, {
merge: true
})
alert("Image uploaded succesfully")
window.location.reload()
} else {
alert("Error when uploading image, try again")
}
}
async uploadImage(uid: string, file: any): Promise<string> {
const fileRef = this._auth._afstg.ref(uid).child("profile-picture");
// Upload file in reference
if (!!file) {
const result = await fileRef.put(file);
return result.ref.fullPath;
}
return ""
}
My problem is, as you can see I have an alert and a reload() after the return function, i tried to run userRef.set without the return function, but it just doesn't work, only way it works is if it is inside the return, and now I can't refresh the page after the data is modified, any ideas why or how I could reload after the return? Already tried try-finally and Prosimes.resolve, neither worked.
I fixed it by using a try-catch and await:
currentImageUrl: string = "";
async uploadPicture(e: any) {
const file = e.target.files[0]
const filePath = this._auth.userData.uid
const task = await this.uploadImage(filePath, file)
if (task) {
//Promise.resolve().then(() => window.location.reload())
this.currentImageUrl = await this._auth._afstg.ref(task).getDownloadURL().toPromise();
const userRef: AngularFirestoreDocument<any> = this._auth._afs.doc(`users/${filePath}`);
const userData: FbUser = {
uid: filePath,
email: this._auth.userData.email,
displayName: this._auth.userData.displayName,
photoURL: this.currentImageUrl,
emailVerified: this._auth.userData.emailVerified
}
try {
await userRef.set(userData, {
merge: true
})
} catch (error) {
console.error("Error modifying user document", error);
}
alert("Image uploaded succesfully")
window.location.reload()
} else {
alert("Error when uploading image, try again")
}
}
async uploadImage(uid: string, file: any): Promise<string> {
const fileRef = this._auth._afstg.ref(uid).child("profile-picture");
// Upload file in reference
if (!!file) {
const result = await fileRef.put(file);
return result.ref.fullPath;
}
return ""
}
I let my users upload their profile image, which shows as a 150x150 pixels circle, I want the image to be resized/compress when he selects it to upload to Firebase. Here is my JavaScript to select and upload the image:
<label for="upload-picture" class="upload-picture button-small">Upload Picture</label>
<input id="upload-picture" type="file" style="display: none" (change)="uploadPicture($event)" accept=".jpeg,.jpg,.png,.svg">
import { Component, OnInit } from '#angular/core';
import { AngularFirestoreDocument } from '#angular/fire/firestore';
import { FbUser } from 'src/app/common/fb-user';
import { AuthService } from 'src/app/services/auth.service';
#Component({
selector: 'app-profile-info',
templateUrl: './profile-info.component.html',
styleUrls: ['./profile-info.component.scss']
})
export class ProfileInfoComponent implements OnInit {
constructor(
public _auth: AuthService,
) { }
ngOnInit(): void {
}
currentImageUrl: string = "";
async uploadPicture(e: any) {
const file = e.target.files[0]
//Here i want to reduce image size
//(image shows as a 150x150 in the DOM so i dont need it to be bigger than that since firebase has a 5GB limit of free storage)
const filePath = this._auth.userData.uid
const task = await this.uploadImage(filePath, file)
if (task) {
this.currentImageUrl = await this._auth._afstg.ref(task).getDownloadURL().toPromise();
const userRef: AngularFirestoreDocument<any> = this._auth._afs.doc(`users/${filePath}`);
const userData: FbUser = {
uid: filePath,
email: this._auth.userData.email,
displayName: this._auth.userData.displayName,
photoURL: this.currentImageUrl,
emailVerified: this._auth.userData.emailVerified
}
userRef.set(userData, {
merge: true
})
alert("Image Uploaded Successfully")
window.location.reload()
} else {
alert("Error when uploading image, try again")
}
}
async uploadImage(uid: string, file: any): Promise<string> {
const fileRef = this._auth._afstg.ref(uid).child("profile-picture");
// Upload file in reference
if (!!file) {
const result = await fileRef.put(file);
return result.ref.fullPath;
}
return ""
}
}
I've tried many approaches, but none seems to be working, anyone got any idea or have done this before?
You can use this, to convert base64 to file blob.
const split = base64Image.split(',');
const type = split[0].replace('data:', '').replace(';base64', '');
const byteString = atob(split[1]);
const arrayBuffer = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(arrayBuffer);
for (let i = 0; i < byteString.length; i += 1) {
ia[i] = byteString.charCodeAt(i);
}
const fileBlob = new Blob([arrayBuffer], {type}); // upload this to firebase.
I'm new to angular and I'm trying to create a download link upon button click that downloads doc file. The file gets downloaded successfully but the content inside is something else which is '[object Object]'. The file path is in my webroot and I'm accessing the file like this:
[Route("api/[Controller]")]
[ApiController]
public class DownloadController : Controller
{
private readonly IWebHostEnvironment _env;
public DownloadController(IWebHostEnvironment env)
{
_env = env;
}
[HttpGet]
public async Task<IActionResult> Download()
{
string filePath = Path.Combine(_env.WebRootPath, "Image\\CV.doc");
using MemoryStream memorystream = new MemoryStream();
using (var stream = new FileStream(filePath, FileMode.Open))
{
await stream.CopyToAsync(memorystream);
}
memorystream.Position = 0;
return File(memorystream, "application/msword", "CV.doc");
}
}
my Shared service.ts. I've changed the observe to 'body' as well.
import { Injectable } from '#angular/core';
import { HttpClient, HttpResponse } from '#angular/common/http';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class SharedService {
cvFilePathURL = "https://localhost:44346/api/Download";
constructor(private http: HttpClient) { }
getCV(): Observable<any> {
return this.http.get(this.cvFilePathURL, {
observe: 'response',
responseType: 'text'
});
}
}
and this is my componenet.ts
import { Component } from '#angular/core';
import { SharedService } from 'src/app/shared.service';
#Component({
selector: 'app-nav-menu',
templateUrl: './nav-menu.component.html',
styleUrls: ['./nav-menu.component.css']
})
export class NavMenuComponent {
constructor(private service: SharedService) { }
isExpanded = false;
collapse() {
this.isExpanded = false;
}
toggle() {
this.isExpanded = !this.isExpanded;
}
public Download(): void {
this.service.getCV().subscribe((data) => {
var newBlob = new Blob([data], { type: "application/msword" });
//For Internet Explorer
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
return window.navigator.msSaveOrOpenBlob(newBlob);
}
//For other browsers:
//Create a link pointing to the ObjectURL containing the blob.
const mainData = window.URL.createObjectURL(newBlob);
var link = document.createElement('a');
link.href = mainData;
link.download = "CV.doc";
link.click();;
});
}
}
target framework - asp.net core 3.1
Angular Cli - 11.0.6
Update:
So I've managed to download file which has actual contents other than [objects Objects]. However, The file is filled with special characters and is 206 pages long. Original document is only 2 pages long.
I think there are some changes you might have to do inside your code or you can check my code what I am going to provide below.
YOU CONTROLLER
[HttpPost]
[Route("get-download-file")]
public HttpResponseMessage GetDownloadFile(dynamic data)
{
// var localFilePath = HttpContext.Current.Server.MapPath("~/Documents/" + (string)data.fileName);
var localFilePath = HttpContext.Current.Server.MapPath((string)data.mappingPath);
string contentType = MimeMapping.GetMimeMapping(Path.GetExtension(localFilePath));
HttpResponseMessage response = null;
try
{
if (!File.Exists(localFilePath))
{
//if file not found than return response as resource not present
response = Request.CreateResponse(HttpStatusCode.Gone);
}
else
{
//if file present than read file
var fStream = new FileStream(localFilePath, FileMode.Open, FileAccess.Read);
//compose response and include file as content in it
response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StreamContent(fStream)
};
//set content header of reponse as file attached in reponse
response.Content.Headers.ContentDisposition =
new ContentDispositionHeaderValue("attachment")
{
FileName = Path.GetFileName(fStream.Name)
};
//set the content header content type as application/octet-stream as it returning file as reponse
response.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
}
}
catch (Exception ex)
{
return response;
}
return response;
}
In your Service(Angular) add that httpPostMethod:
public httpPostFile(url: string, body: any): Observable<Blob> {
return this.httpClient.post<Blob>(this.baseUri + url, body, { responseType: 'blob' as 'json' })
.pipe(
catchError(this.handleClientError)
);
}
Finally, you have to call that method from angular. Using code below->
let tempBlob: any;
let contentFileType= 'txt'; //you can make that file type dynamic using some addional code.
const data = {
fileName: this.fileName,
mappingPath: '~/Documents/' + this.fileName
};
this.apiService.httpPostFile('download/get-download-file/', data)
.subscribe(fileData => {
tempBlob = new Blob([fileData], { type: contentFileType });
},
(error) => {
}, () => {
const blob: Blob = new Blob(tempBlob, { type: contentFileType });
const fileName: string = this.filedlName;
const objectUrl: string = URL.createObjectURL(blob);
const a: HTMLAnchorElement = document.createElement('a') as HTMLAnchorElement;
a.href = objectUrl;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(objectUrl);
});
Note: There can be more efficient way to download a file in angular and ASP MVC Web API. But this way it worked for me. you can try it. If you find any difficulties than feel free to ask. Thank you.
One of my files have is creating a new Worker, and it uses file-loader module in order to do so.
Here's my code:
import * as workerPath from 'file-loader?name=[name].js!./worker';
class VideoPlayerWorker {
private worker: Worker;
constructor({ onMessageCallback }: { onMessageCallback: Function }) {
this.worker = new Worker(workerPath);
this.initHandleMessage({ onMessageCallback });
}
initTimer = (): void => {
this.postMessage({ type: 'WORKER_INIT_TIMER' });
};
postMessage = (message: ApeVideo.VideoWorkerEventMessage): void => {
this.worker.postMessage(message);
};
initHandleMessage = ({ onMessageCallback }: { onMessageCallback: Function }): void => {
this.worker.addEventListener('message', ({ data }: { data: ApeVideo.VideoWorkerEventMessage }): void => {
onMessageCallback(data.type);
});
};
destroy = (): void => {
this.worker.terminate();
};
}
export default VideoPlayerWorker;
The problem is when running jest, i get an error Cannot find module 'file-loader?name=[name].js!./worker' from 'playerWorkerGetaway.ts'
How can i work around this?