Implementing the File Transfer
The basic idea is an automatic transfer of image files from one computer (in this case a Raspi) to another computer. Of course you could do the file transfer just as well with FTP or HTTP, but if you already have MQTT in use, you save the setup and implementation of the FTP and HTTP servers. By the way, the implementation allows the transfer of arbitrary files, not only image files.
Commuication Principle
The example uses two Python classes: an ImageSender and an ImageReceiver. The ImageSender reads all files with the extension "jpg" from a directory and then sends first the name of the file (as string) and then the content base64 encoded.
The topics "ImageReceiver/start" and "ImageReceiver/stop" are used to enable the ImageReceiver to actively start and stop the file transfer.
File Transfer Implementation
For the transfer of binary files it is always advantageous to use a transfer format that is not dependent on any code pages (UTF-8 / 16 / ..). A good method is for instance base64, Base64 is a method for encoding 8-bit binary data (e.g. executable programs, ZIP files, or images) into a string consisting only of readable, codepage-independent ASCII characters.
Source code for reading and sending a file as base64 bytes:
file = open(" ... your pathname ... ", 'rb') filecontent = file.read() # encode base64 base64_bytes = base64.b64encode(filecontent) result = self.client.publish("ImageSender/content", base64_bytes, 0)
Source code for receiving, decoding and saving a file:
file = open(" ... your pathname ... ", "wb") content = str(message.payload.decode('utf-8')) image = base64.b64decode(content) file.write(image) file.close()
Complete Source Code
import base64 import os import sys import time import paho.mqtt.client as mqtt # ImageSender # A ImageSender waits for a start message , retrieves all images in a file and send all # files with names to an ImageReceiver class ImageSender: def __init__(self): try: self.client = mqtt.Client("ImageSender") # create new instance self.client.connect("192.168.2.110") # connect to your broker self.client.subscribe("ImageReceiver/start") self.client.subscribe("ImageReceiver/stop") # start loop für MQTT client self.client.loop_start() # start the loop # this is the directory containing the images self.dir = "/home/shares/pi/imagesender/pictures" # callback for incoming MQTT Messages self.client.on_message = self.messageArrived # run until a stop message arriived self.keeponrunning = True self.run() except Exception: print("Connection to Broker failed ... shutting down.") sys.exit() # run as long as there is no stop message from the imageReceiver def run(self): while(self.keeponrunning): time.sleep(1) # disconnect from broker when application stops self.client.disconnect() # checks for incoming messages def messageArrived(self, client, userdata, message): # start ot stop? if message.topic == "ImageReceiver/start": self.sendImages() if message.topic == "ImageReceiver/stop": print("Received stop message, image service will stop ...") self.keeponrunning = False def sendImages(self): # iterate directory and send all image files (jpg) in the directory filelist = [] # iterating over all files which end with "jpg" for file in os.listdir(self.dir): if file.endswith('jpg'): filelist.append(file) else: continue print(filelist) for name in filelist: # send file name prior to file content self.client.publish("ImageSender/filename", name) # open the file for binary read file = open("./pictures/" + name, 'rb') filecontent = file.read() # encode base64 base64_bytes = base64.b64encode(filecontent) result = self.client.publish("ImageSender/content", base64_bytes, 0) msg_status = result[0] if msg_status == 0: print("file ", name, "sent ...") else: print("Failed to send file", name) # start sender sender = ImageSender()
import base64 import sys import time import paho.mqtt.client as mqtt # ImageReceiver # A ImageReceiver sends a start message to an ImageSender, waits for the files # and saves them to a local directory class ImageReceiver: def __init__(self): try: self.client = mqtt.Client("ImageReceiver") # create new instance self.client.connect("192.168.2.110") # connect to your broker self.client.subscribe("ImageSender/filename") self.client.subscribe("ImageSender/content") # start loop für MQTT client self.client.loop_start() # start the loop # this is the directory containing the images self.imagedir = "./pictures" # callback for incoming MQTT Messages self.client.on_message = self.imageDataArrived # send an empty start Message as soon as the ImageReceiver object was created self.client.publish("ImageReceiver/start", "") # press ENTER to stop receiver -> send stop message to sender input("Press ENTER to stop image receiver...") print("Sending stop message to sender ...") self.client.publish("ImageReceiver/stop", "") time.sleep(1) self.client.disconnect() except Exception: print("Connection to Broker failed ... shutting down.") sys.exit() def imageDataArrived(self, client, userdata, message): data = message.payload.decode("utf-8") # filename or file content? if message.topic == "ImageSender/filename": self.nextFileName = data if message.topic == "ImageSender/content": # open file for write access file = open(self.imagedir + "/" + self.nextFileName, "wb") content = str(message.payload.decode('utf-8')) image = base64.b64decode(content) file.write(image) file.close() self.nextFileName = "" sender = ImageReceiver()