Browse Source

Refactored the `FileService.parseFromUrl` method to return the filepath instead of a stream, and updated the `routes/index.js` file to handle the new return value

node10
Onja 11 months ago
parent
commit
cd747af78a
  1. 31
      src/models/file.js
  2. 24
      src/routes/index.js
  3. 15
      src/services/file.js

31
src/models/file.js

@ -18,15 +18,6 @@ const { basedir } = require('../config/constants');
const dest = path.join(basedir, 'public/csv'); const dest = path.join(basedir, 'public/csv');
// Create a generateFilePath function witch returns a path with a filename and datetime
function generateFilePath(filename) {
return {
filepath: path.join(dest, `${filename}-${Date.now()}.csv`),
generatedpath: path.join(dest, `${filename}-generated-${Date.now()}.csv`),
};
}
// Create a class File that extends EventEmitter // Create a class File that extends EventEmitter
class File { class File {
@ -38,7 +29,14 @@ class File {
this.url = url; this.url = url;
} }
// Create a generateFilePath function witch returns a path with a filename and datetime
generateFilePath(filename) {
const date = new Date();
return {
filepath: path.join(dest, `${filename}-${date.toISOString().split('T')[0]}.csv`),
generatedpath: path.join(dest, `${filename}-generated-${date.toISOString().split('T')[0]}.csv`),
};
}
/** /**
* Download a file from a url * Download a file from a url
@ -47,7 +45,7 @@ class File {
async download() { async download() {
const url = URL.parse(this.url); const url = URL.parse(this.url);
this.filename = slugify(url.hostname, { lower: true }); this.filename = slugify(url.hostname, { lower: true });
const { filepath, generatedpath } = generateFilePath(this.filename); const { filepath, generatedpath } = this.generateFilePath(this.filename);
this.filepath = filepath; this.filepath = filepath;
this.generatedpath = generatedpath; this.generatedpath = generatedpath;
@ -121,13 +119,14 @@ class File {
// create a parse method which read the file and return a stream // create a parse method which read the file and return a stream
parse(columns) { parse(columns) {
const stream = new PassThrough(); return new Promise((resolve, reject) => {
const fileStream = fs.createWriteStream(this.generatedpath); const fileStream = fs.createWriteStream(this.generatedpath);
// check if columns is valid // check if columns is valid
if (!columns || !columns.length) { if (!columns || !columns.length) {
// return Promise.reject(new Error('Invalid columns')); // return Promise.reject(new Error('Invalid columns'));
emitter.emit('parse.error', { url: this.url, filepath: this.filepath, error: 'Invalid columns' }); emitter.emit('parse.error', { url: this.url, filepath: this.filepath, error: 'Invalid columns' });
reject(new Error('Invalid columns'));
return false; return false;
} }
@ -163,14 +162,12 @@ class File {
// Emit a parse.start event with the url and filepath // Emit a parse.start event with the url and filepath
emitter.emit('parse.start', { url: this.url, filepath: this.filepath, headers, result: result }); emitter.emit('parse.start', { url: this.url, filepath: this.filepath, headers, result: result });
stream.write(result.join(';') + "\n");
fileStream.write(result.join(';') + "\n"); fileStream.write(result.join(';') + "\n");
}) })
.on('data', (row) => { .on('data', (row) => {
// Emit a parse.data event with the url, filepath and data // Emit a parse.data event with the url, filepath and data
let result = this.processRow(row, columnsIndex, columnsFiltered); let result = this.processRow(row, columnsIndex, columnsFiltered);
emitter.emit('parse.data', { url: this.url, filepath: this.filepath, data: row, result, index: count }); emitter.emit('parse.data', { url: this.url, filepath: this.filepath, data: row, result, index: count });
stream.write(result.join(';') + "\n");
fileStream.write(result.join(';') + "\n"); fileStream.write(result.join(';') + "\n");
count++; count++;
}) })
@ -179,16 +176,16 @@ class File {
emitter.emit('parse.error', { url: this.url, filepath: this.filepath, error: err.message }); emitter.emit('parse.error', { url: this.url, filepath: this.filepath, error: err.message });
fileStream.close(); fileStream.close();
reject(err);
fs.unlink(this.generatedpath, () => {}); fs.unlink(this.generatedpath, () => {});
}) })
.on('end', () => { .on('end', () => {
// Emit a parse.end event with the url and filepath // Emit a parse.end event with the url and filepath
stream.end();
fileStream.close(); fileStream.close();
resolve(this.generatedpath);
emitter.emit('parse.end', { url: this.url, filepath: this.filepath, count: count - 1, generated: path.basename(this.generatedpath) }); emitter.emit('parse.end', { url: this.url, filepath: this.filepath, count: count - 1, generated: path.basename(this.generatedpath) });
}); });
});
return stream;
} }
/** /**

24
src/routes/index.js

@ -105,7 +105,7 @@ router.get('/', async function(req, res, next) {
return res; return res;
}); });
router.post('/', async function(req, res, next) { router.post('/', function(req, res, next) {
// const url = 'https://bodacc-datadila.opendatasoft.com/api/explore/v2.1/catalog/datasets/annonces-commerciales/exports/csv?lang=fr&refine=publicationavis%3A%22A%22&refine=publicationavis_facette%3A%22Bodacc%20A%22&refine=familleavis_lib%3A%22Ventes%20et%20cessions%22&timezone=Asia%2FBaghdad&use_labels=true&delimiter=%3B'; // const url = 'https://bodacc-datadila.opendatasoft.com/api/explore/v2.1/catalog/datasets/annonces-commerciales/exports/csv?lang=fr&refine=publicationavis%3A%22A%22&refine=publicationavis_facette%3A%22Bodacc%20A%22&refine=familleavis_lib%3A%22Ventes%20et%20cessions%22&timezone=Asia%2FBaghdad&use_labels=true&delimiter=%3B';
// get url from form // get url from form
@ -120,22 +120,16 @@ router.post('/', async function(req, res, next) {
return res.status(500).send('Invalid columns'); return res.status(500).send('Invalid columns');
} }
let stream = null; fileService.parseFromUrl(url, columns)
try { .then((filepath) => {
stream = await fileService.parseFromUrl(url, columns); res.send({
} catch (err) { success: true
})
})
.catch(err => {
console.error('routes [/] error', err.message); console.error('routes [/] error', err.message);
}
if ( !stream ) {
return res.status(500).send('Invalid stream'); return res.status(500).send('Invalid stream');
} });
res.setHeader('Content-Disposition', 'attachment; filename="mon_fichier.csv"');
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
stream.pipe(res);
// res.render('index', { title: 'Express' });
}); });
module.exports = router; module.exports = router;

15
src/services/file.js

@ -29,14 +29,15 @@ class FileService {
const file = new File(url); const file = new File(url);
const filepath = await file.download(); const filepath = await file.download();
const stream = file.parse(columns); return file.parse(columns)
if ( !stream ) { .then((filepath) => {
emitter.emit('parseFromUrl.error', { url, columns, error: 'Invalid stream' });
return Promise.reject(new Error('Invalid stream'));
}
emitter.emit('parseFromUrl.end', { url, columns, filepath }); emitter.emit('parseFromUrl.end', { url, columns, filepath });
return Promise.resolve(stream); return filepath;
})
.catch((err) => {
emitter.emit('parseFromUrl.error', { url, columns, error: err.message });
return err;
});
} }
/** /**

Loading…
Cancel
Save