Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Logs
logs
*.log
.DS_Store

# Runtime data
pids
Expand Down Expand Up @@ -31,4 +32,4 @@ node_modules
test/temp/*.*

lib/scripts/serverScript.js
lib/scripts/standaloneScript.js
lib/scripts/standaloneScript.js
41 changes: 38 additions & 3 deletions lib/conversion.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,45 @@ function convert(conversionOptions, cb) {
delete opt.html;

if (options.strategy === "phantom-server")
return require("./serverStrategy.js")(options, opt, id, cb);
return require("./serverStrategy.js")(options, opt, id, checkIfShouldRemoveTempFile);
if (options.strategy === "dedicated-process")
return require("./dedicatedProcessStrategy.js")(options, opt, id, cb);
return require("./dedicatedProcessStrategy.js")(options, opt, id, checkIfShouldRemoveTempFile);

// verify if we should remove the pdf temp file,
// only if the user has not done any intention to consume the file stream
function checkIfShouldRemoveTempFile(err, resp) {
var shouldRemoveTempFile = false;

if (err) {
return cb(err);
}

if (resp.stream == null) {
return cb(null, resp);
}

// executing the callback
cb(null, resp);

// on the next tick of the event loop verify if file stream is consumed..
process.nextTick(function() {
if (resp.stream._readableState.flowing === false &&
resp.stream._readableState.reading === false &&
resp.stream._readableState.calledRead === false &&
resp.stream._readableState.needReadable === false) {
// in node 0.10.x these properties tell if a stream is not consumed either on flowing or paused mode
shouldRemoveTempFile = true;
} else if (resp.stream._readableState.flowing === null &&
resp.stream._readableState.needReadable === false) {
// in node 0.12.x and 4.x.x, 5.x.x, 6.x.x, 7.x.x ... these properties tell if a stream is not consumed either on flowing or paused mode
shouldRemoveTempFile = true;
}

if (shouldRemoveTempFile) {
fs.unlink(resp.stream.path, function() { /* ignore any error when deleting the file */ });
}
});
}

cb(new Error("Unsupported strategy " + options.strategy));
});
Expand All @@ -94,4 +130,3 @@ module.exports = function (opt) {

return convert;
};

9 changes: 7 additions & 2 deletions lib/dedicatedProcessStrategy.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var path = require("path"),
childProcess = require('child_process'),
fs = require("fs");
fs = require("fs"),
removeTempFileWhenConsumed = require('./removeTempFileWhenConsumed');


module.exports = function(options, requestOptions, id, cb) {
Expand Down Expand Up @@ -58,8 +59,12 @@ module.exports = function(options, requestOptions, id, cb) {
m.timestamp = new Date(m.timestamp)
})

var outputStream = fs.createReadStream(requestOptions.output);

removeTempFileWhenConsumed(requestOptions.output, outputStream);

cb(null, {
stream: fs.createReadStream(requestOptions.output),
stream: outputStream,
numberOfPages: response.numberOfPages,
logs: response.logs
});
Expand Down
34 changes: 34 additions & 0 deletions lib/removeTempFileWhenConsumed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var fs = require('fs'),
firstEvent = require('ee-first');

module.exports = function removeTempFileWhenConsumed(filepath, fileStream) {
var thunk = firstEvent([
[fileStream, 'close', 'end', 'error']
], function(err, stream, eventName, args) {
var shouldPropagateStreamError = false;

if (err || eventName === 'error') {
// if there is only one listener for the 'error' event (our handler)
// we should propagate the error,
// if there is more than one listener it means that the user is listening for the event 'error'
// in the stream, so it's user responsibility to handle the error correctly
if (fileStream.listeners('error').length === 1) {
shouldPropagateStreamError = true;
}
}

// clean up event listeners on stream after the callback has been executed
thunk.cancel();

// clean up temp file
deleteFile(filepath);

if (shouldPropagateStreamError) {
fileStream.emit('error', err);
}
});
};

function deleteFile(filepath) {
fs.unlink(filepath, function() { /* ignore any error when deleting the file */ });
}
9 changes: 7 additions & 2 deletions lib/serverStrategy.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var Phantom = require("phantom-workers"),
fs = require("fs"),
_ = require("lodash");
_ = require("lodash"),
removeTempFileWhenConsumed = require('./removeTempFileWhenConsumed');

var phantoms = {};

Expand Down Expand Up @@ -61,8 +62,12 @@ module.exports = function(options, requestOptions, id, cb) {
m.timestamp = new Date(m.timestamp)
})

var outputStream = fs.createReadStream(requestOptions.output);

removeTempFileWhenConsumed(requestOptions.output, outputStream);

cb(null, {
stream: fs.createReadStream(requestOptions.output),
stream: outputStream,
numberOfPages: res.numberOfPages,
logs: res.logs
});
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"url": "git@github.com:pofider/phantom-html-to-pdf.git"
},
"dependencies": {
"ee-first": "1.1.1",
"in-publish": "2.0.0",
"lodash": "4.13.1",
"phantom-workers": "0.4.0",
Expand Down
61 changes: 60 additions & 1 deletion test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,65 @@ describe("phantom html to pdf", function () {
});
});

it("should remove temp pdf file when stream is consumed in flowing mode", function (done) {
conversion("foo", function (err, res) {
if (err)
return done(err);

res.numberOfPages.should.be.eql(1);

// switching the stream to flowing mode, to consume its content
res.stream.on('data', function() {});

setTimeout(function() {
// temp pdf file should be removed after being consumed
should(fs.existsSync(res.stream.path)).be.eql(false);

done();
}, 300);
});
});

it("should remove temp pdf file when stream is consumed in paused mode", function (done) {
conversion("foo", function (err, res) {
if (err)
return done(err);

res.numberOfPages.should.be.eql(1);

// consuming the stream from paused mode
res.stream.on('readable', function() {
var chunk;

while (null !== (chunk = res.stream.read())) {
}
});

setTimeout(function() {
// temp pdf file should be removed after being consumed
should(fs.existsSync(res.stream.path)).be.eql(false);

done();
}, 300);
});
});

it("should remove temp pdf file when not consumed", function (done) {
conversion("foo", function (err, res) {
if (err)
return done(err);

res.numberOfPages.should.be.eql(1);

setTimeout(function() {
// temp pdf file should be removed after being consumed
should(fs.existsSync(res.stream.path)).be.eql(false);

done();
}, 300);
});
});

it("should work with multiple phantom paths", function (done) {
conversion({ html: "foo", phantomPath: phantomjs.path}, function (err, res) {
if (err)
Expand Down Expand Up @@ -261,4 +320,4 @@ describe("phantom html to pdf", function () {
}
}
};
});
});