Skip to content

Commit 5ddfcff

Browse files
authored
Update docs, handler and unpacking for new version of lo (#28)
Update docs, handler and unpacking for new version of lo
2 parents fe8095e + 0cc0bc5 commit 5ddfcff

File tree

5 files changed

+120
-51
lines changed

5 files changed

+120
-51
lines changed

README.md

+35-8
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
This repo contains code used to run the [online demo](https://vladholubiev.com/serverless-libreoffice).
1414

15-
1615
```
1716
├── compile.sh <-- commands used to compile LibreOffice for Lambda
1817
├── infra <-- terraform config to deploy example Lambda
@@ -35,23 +34,23 @@ Compiled and ready to use archive can be downloaded under [Releases section](htt
3534

3635
# How to compile by yourself
3736

38-
> Check out a comprehensive [step-by-step tutorial](STEP_BY_STEP.md) from 0 to deployed function.
37+
> Check out a comprehensive [step-by-step tutorial](STEP_BY_STEP.md) from 0 to deployed function.
3938
4039
1. Go to [Lambda Execution Environment and Available Libraries](https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html) page to get the latest AMI id
41-
2. Click on [this link](https://console.aws.amazon.com/ec2/v2/home#Images:visibility=public-images;search=amzn-ami-hvm-2017.03.1.20170812-x86_64-gp2) to get AMI id for your region
40+
2. Click on [this link](https://console.aws.amazon.com/ec2/v2/home#Images:visibility=public-images;search=amzn-ami-hvm-2017.03.1.20170812-x86_64-gp2) to get AMI id for your region
4241
3. Spin up a `c5.2xlarge` spot instance with ~ 100 GB of storage attached
4342
4. Follow the steps in `compile.sh` file in the repo
4443

4544
# Help
4645

47-
* [List of RPM Packages available in AWS Lambda](https://gist.github.com/vladgolubev/1dac4ed47a5febf110c668074c6b671c)
48-
* [List of Libraries available in AWS Lambda](https://gist.github.com/vladgolubev/439559fc7597a4fb51eaa9e97b72f319)
46+
- [List of RPM Packages available in AWS Lambda](https://gist.github.com/vladgolubev/1dac4ed47a5febf110c668074c6b671c)
47+
- [List of Libraries available in AWS Lambda](https://gist.github.com/vladgolubev/439559fc7597a4fb51eaa9e97b72f319)
4948

5049
# Related Projects
5150

52-
* [Docker in AWS Lambda](https://github.com/vladgolubev/docker-in-aws-lambda)
53-
* [NPM package with bundled LibreOffice for Lambda (85 MB)](https://github.com/shelfio/aws-lambda-libreoffice)
54-
* [Lambda Layer with LibreOffice](https://github.com/shelfio/libreoffice-lambda-layer)
51+
- [Docker in AWS Lambda](https://github.com/vladgolubev/docker-in-aws-lambda)
52+
- [NPM package with bundled LibreOffice for Lambda (85 MB)](https://github.com/shelfio/aws-lambda-libreoffice)
53+
- [Lambda Layer with LibreOffice](https://github.com/shelfio/libreoffice-lambda-layer)
5554

5655
# How To Help
5756

@@ -80,6 +79,34 @@ Here is the list of: [available RPM packages](https://gist.github.com/vladgolube
8079
and [libraries](https://gist.github.com/vladgolubev/439559fc7597a4fb51eaa9e97b72f319)
8180
available in AWS Lambda Environment, which can be helpful.
8281

82+
You can also use multi compression level, with upx and then decompress after brotli.
83+
84+
## Testing
85+
86+
Update repo for testing. Return before S3 for example, hardcode or generate files to convert and setup variables. Then simply run:
87+
88+
```
89+
docker run \
90+
-v "\$PWD":/var/task \
91+
lambci/lambda:nodejs12.x src/handler.handler
92+
```
93+
94+
After successful execution, get the resulted files to check the pdfs.
95+
96+
```
97+
docker ps -a
98+
```
99+
100+
Find exect container id.
101+
102+
Then execute
103+
104+
```
105+
docker cp containerId:/tmp/filename.pdf ./filename.pdf
106+
```
107+
108+
Then check your results locally
109+
83110
## License
84111

85112
MIT © [Vlad Holubiev](https://vladholubiev.com)

src/handler.js

+12-17
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
1-
const {unpackArchive} = require('./libreoffice');
2-
const {convertFileToPDF} = require('./logic');
1+
const { unpack } = require("./libreoffice");
2+
const { convertFileToPDF } = require("./logic");
33

4-
unpackArchive();
5-
6-
module.exports.handler = (event, context, cb) => {
4+
module.exports.handler = async (event, context, cb) => {
5+
await unpack();
76
if (event.warmup) {
87
return cb();
98
}
109

11-
const {filename, base64File} = JSON.parse(event.body);
12-
13-
return convertFileToPDF(base64File, filename)
14-
.then(pdfFileURL => {
15-
return cb(null, {
16-
headers: {
17-
'Access-Control-Allow-Origin': 'https://vladholubiev.com'
18-
},
19-
body: JSON.stringify({pdfFileURL})
20-
});
21-
})
22-
.catch(cb);
10+
const { filename, base64File } = JSON.parse(event.body);
11+
const pdfFileURL = await convertFileToPDF(base64File, filename).catch(cb);
12+
return cb(null, {
13+
headers: {
14+
"Access-Control-Allow-Origin": "https://vladholubiev.com"
15+
},
16+
body: JSON.stringify({ pdfFileURL })
17+
});
2318
};

src/libreoffice.js

+49-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
const {execSync} = require('child_process');
2-
const {readFileSync} = require('fs');
3-
const path = require('path');
1+
const { execSync } = require("child_process");
2+
const { readFileSync } = require("fs");
3+
const tar = require("tar-fs");
4+
const zlib = require("zlib");
5+
const path = require("path");
46

5-
const convertCommand = `./instdir/program/soffice --headless --invisible --nodefault --nofirststartwizard --nolockcheck --nologo --norestore --convert-to pdf --outdir /tmp`;
7+
const convertCommand = `export HOME=/tmp && ./instdir/program/soffice.bin --headless --norestore --invisible --nodefault --nofirststartwizard --nolockcheck --nologo --convert-to "pdf:writer_pdf_Export" --outdir /tmp`;
68

79
/**
810
* Converts a document to PDF from url by spawning LibreOffice process
@@ -13,7 +15,12 @@ module.exports.convertToPDF = function convertToPDF(inputFilename) {
1315
console.log(`[convertToPDF][file:${inputFilename}]`);
1416
const pdfFilename = getPDFFilename(inputFilename);
1517

16-
execSync(`cd /tmp && ${convertCommand} ${inputFilename}`);
18+
try {
19+
// First run will produce predictable error, because of unknown issues
20+
execSync(`cd /tmp && ${convertCommand} ${inputFilename}`);
21+
} catch (e) {
22+
execSync(`cd /tmp && ${convertCommand} ${inputFilename}`);
23+
}
1724
console.log(`[converted]`);
1825

1926
const pdfFileBuffer = readFileSync(`/tmp/${pdfFilename}`);
@@ -25,10 +32,44 @@ module.exports.convertToPDF = function convertToPDF(inputFilename) {
2532
};
2633

2734
function getPDFFilename(inputFilename) {
28-
const {name} = path.parse(inputFilename);
35+
const { name } = path.parse(inputFilename);
2936
return `${name}.pdf`;
3037
}
3138

32-
module.exports.unpackArchive = function unpackArchive() {
33-
execSync(`cd /tmp && tar -xf /var/task/lo.tar.gz`);
39+
module.exports.unpack = function({
40+
inputPath = `/var/task/lo.tar.br`,
41+
outputBaseDir = `/tmp`,
42+
outputPath = `/tmp/instdir`
43+
}) {
44+
return new Promise((resolve, reject) => {
45+
let input = path.resolve(inputPath);
46+
let output = outputPath;
47+
48+
if (fs.existsSync(output) === true) {
49+
return resolve(output);
50+
}
51+
52+
const source = fs.createReadStream(input);
53+
const target = tar.extract(outputBaseDir);
54+
55+
source.on("error", error => {
56+
return reject(error);
57+
});
58+
59+
target.on("error", error => {
60+
return reject(error);
61+
});
62+
63+
target.on("finish", () => {
64+
fs.chmod(output, "0755", error => {
65+
if (error) {
66+
return reject(error);
67+
}
68+
69+
return resolve(output);
70+
});
71+
});
72+
73+
source.pipe(zlib.createBrotliDecompress()).pipe(target);
74+
});
3475
};

src/logic.js

+16-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
const {writeFileSync} = require('fs');
2-
const {convertToPDF} = require('./libreoffice');
3-
const {uploadPDF} = require('./s3');
1+
const { writeFileSync } = require("fs");
2+
const { convertToPDF } = require("./libreoffice");
3+
const { uploadPDF } = require("./s3");
44

55
const MAX_FILE_SIZE = 5 * 1024 * 1024;
66

@@ -10,31 +10,36 @@ const MAX_FILE_SIZE = 5 * 1024 * 1024;
1010
* @param filename {String} Name of file to convert
1111
* @return {Promise.<String>} URL of uploaded file on S3
1212
*/
13-
module.exports.convertFileToPDF = function convertFileToPDF(base64File, filename) {
14-
console.log(`[start][file:${filename}][buffer:${base64File.slice(0, 16)}...]`);
15-
16-
const fileBuffer = new Buffer(base64File, 'base64');
13+
module.exports.convertFileToPDF = function convertFileToPDF(
14+
base64File,
15+
filename
16+
) {
17+
console.log(
18+
`[start][file:${filename}][buffer:${base64File.slice(0, 16)}...]`
19+
);
20+
21+
const fileBuffer = new Buffer(base64File, "base64");
1722
console.log(`[size:${fileBuffer.length}]`);
1823

1924
const fileError = validate(fileBuffer);
2025
if (fileError) {
21-
return fileError;
26+
return fileError;
2227
}
2328

2429
writeFileSync(`/tmp/${filename}`, fileBuffer);
2530
console.log(`[written]`);
2631

27-
const {pdfFilename, pdfFileBuffer} = convertToPDF(filename);
32+
const { pdfFilename, pdfFileBuffer } = convertToPDF(filename);
2833

2934
return uploadPDF(pdfFilename, pdfFileBuffer);
3035
};
3136

3237
function validate(fileBuffer) {
3338
if (fileBuffer.length > MAX_FILE_SIZE) {
34-
return Promise.reject(new Error('File is too large'));
39+
return Promise.reject(new Error("File is too large"));
3540
}
3641

3742
if (fileBuffer.length < 4) {
38-
return Promise.reject(new Error('File is too small'));
43+
return Promise.reject(new Error("File is too small"));
3944
}
4045
}

src/s3.js

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
const {execSync} = require('child_process');
2-
const {S3} = require('aws-sdk');
1+
const { execSync } = require("child_process");
2+
const { S3 } = require("aws-sdk");
33

4-
const s3 = new S3({region: 'us-east-1'});
4+
const s3 = new S3({ region: "us-east-1" });
55

66
/**
77
* Uploads converted PDF file to S3 bucket
@@ -15,13 +15,14 @@ function uploadPDF(filename, fileBuffer) {
1515
Bucket: process.env.S3_BUCKET_NAME,
1616
Key: `tmp/pdf/${filename}`,
1717
Body: fileBuffer,
18-
ACL: 'public-read',
19-
ContentType: 'application/pdf'
18+
ACL: "public-read",
19+
ContentType: "application/pdf"
2020
};
2121

22-
return s3.upload(options)
22+
return s3
23+
.upload(options)
2324
.promise()
24-
.then(({Location}) => Location)
25+
.then(({ Location }) => Location)
2526
.then(Location => {
2627
execSync(`rm /tmp/${filename}`);
2728
console.log(`[removed]`);

0 commit comments

Comments
 (0)