Skip to content

Instantly share code, notes, and snippets.

@martic
Forked from flostellbrink/Flash Air Media Uploader.md
Created March 12, 2019 03:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save martic/753ed9466f39ab13bee56d02c6dda432 to your computer and use it in GitHub Desktop.
Save martic/753ed9466f39ab13bee56d02c6dda432 to your computer and use it in GitHub Desktop.
FlashAir uploader is a script that iterates over all photos and movies saved on an SD card and uploads them to an HTTP endpoint. Uploaded files are renamed. A tiny node
-- FlashAir Lua script
-- Copy to 'Lua' folder on your SD card
-- Set as RUN_SCRIPT/SD_EVENT in your SD_WLAN/CONFIG file:
-- LUA_RUN_SCRIPT=/Lua/upload.lua
-- LUA_SD_EVENT=/Lua/upload.lua
local httpendpoint = 'http://192.168.1.13:3333/upload'
local folder = '/DCIM'
-- Recursively iterate through directories
function walk_directory(folder, extension)
for file in lfs.dir(folder) do
-- Not sure if this is necessary, but it keeps 'LUA : not enough memory' out of the log
collectgarbage()
-- Find file path and type
local path = folder .. "/" .. file
local attr = lfs.attributes(path)
if attr.mode == "file" then
handle_file(file, path, extension)
elseif attr.mode == "directory" then
walk_directory(path, extension)
end
end
end
-- Check file for upload, upload and rename it
function handle_file(file, path, extension)
-- Find files not yet marked as "_SAVED"
if string.sub(path, -4, -1) ~= extension then return end
if string.sub(path, -10, -5) == "_SAVED" then return end
-- Upload file to HTTP server
local boundary = '--61141483716826'
local contenttype = 'multipart/form-data; boundary=' .. boundary
-- <!--WLANSDFILE--> will be replaced with file content
local body = '--'.. boundary .. '\r\n'
..'Content-Disposition: form-data; name=\"file\"; filename=\"'..file ..'\"\r\n'
..'Content-Type: text/plain\r\n'
..'\r\n'
..'<!--WLANSDFILE-->\r\n'
..'--' .. boundary .. '--\r\n'
-- Exclude size of placeholder
local size = lfs.attributes(path, 'size') + string.len(body) - 17
-- Execute request (returns response body, response code, response header)
local b, c, h = fa.request {
url = httpendpoint,
method = 'POST',
headers = {['Content-Length'] = tostring(size),
['Content-Type'] = contenttype},
file = path,
body = body
}
b, h = nil
collectgarbage()
if (c ~= 200) then return end
-- Mark file as "_SAVED"
fa.rename(path, string.sub(path, 0, -5) .. '_SAVED' .. string.sub(path, -4, -1))
end
-- Keep checking WLAN status until it is up or 30 seconds passed
for i = 1, 300 do
-- Allow card to connect with WLAN
sleep(100)
-- Check if WLAN is up
if fa.WlanLink() == 1 then
-- Upload pictures first
walk_directory(folder, '.JPG')
-- Upload movies later, they are much larger and might fail
walk_directory(folder, '.MOV')
break
end
end
/* File Receiver
* Handles uploads from flashair.
* Install dependencies with npm using the package.json file: https://docs.npmjs.com/cli/install
* Start with node: https://nodejs.org/en/
*/
var util = require('util')
var spawn = require('child_process').spawn;
var http = require('http');
var formidable = require('formidable');
var fs = require('fs');
var winston = require('winston');
var uploadPath = '/media/main/Bilder/FlashAir/';
// Create log file
winston.add(winston.transports.File, { filename: '/home/pi/upload/winston.json' });
// Redirect all errors into log file
function errorHandler(err) {
if(err) {
winston.error(err);
throw err;
}
}
winston.info('Starting file receiver');
http.createServer(function (req, res) {
// Handle upload requests
if (req.url == '/upload') {
winston.info('Upload request');
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
// console.log(util.inspect(files, false, null));
if(!files.file) {
winston.error('No file in request');
res.writeHead(400);
res.end('No file in request');
return;
}
var readStream = fs.createReadStream(files.file.path);
var writeStream = fs.createWriteStream(uploadPath + files.file.name);
winston.info('File uploaded to: ' + files.file.path)
readStream.on('error', errorHandler);
writeStream.on('error', errorHandler);
readStream.on('close', function () {
winston.info('File moved to destination with name: ' + files.file.name)
res.end('Done');
fs.unlink(files.file.path, function(err) {
errorHandler(err);
if(err) return;
winston.info('Removed temporary file');
});
/* I run a backup and sort script at this point.
* Feel free to uncomment this and insert your own.
winston.info('Running backup and sorting');
spawn('bash', [ '/home/pi/backup/backup.sh' ]);
*/
});
readStream.pipe(writeStream);
});
} else {
// For other requests show a basic html upload form
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<form action="upload" method="post" enctype="multipart/form-data">');
res.write('<input type="file" name="file"><br>');
res.write('<input type="submit">');
res.write('</form>');
return res.end();
}
}).listen(3333);
{
"name": "file_receiver",
"version": "0.0.0",
"description": "receive files uploaded by flashair",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "flo",
"license": "ISC",
"dependencies": {
"formidable": "^1.1.1",
"winston": "^2.3.1"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment