Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Berry webserver: allow to content_send bytes #21479

Merged
merged 1 commit into from
May 24, 2024

Conversation

Staars
Copy link
Contributor

@Staars Staars commented May 23, 2024

Description:

This small addition makes it possible to serve arbitrary data like images or any other file with binary data.

Typically prepended with a call to content_open to declare the MIME type:

    var data = bytes("0011223344")
    webserver.content_open(200,"application/octet-stream")
    webserver.content_send(data)

Longer demo in the comments.

Checklist:

  • The pull request is done against the latest development branch
  • Only relevant files were touched
  • Only one feature/fix was added per PR and the code change compiles without warnings
  • The code change is tested and works with Tasmota core ESP8266 V.2.7.6
  • The code change is tested and works with Tasmota core ESP32 V.3.0.0
  • I accept the CLA.

NOTE: The code change must pass CI tests. Your PR cannot be merged unless tests pass

@Staars
Copy link
Contributor Author

Staars commented May 23, 2024

Very simple demo to download the files from the root folder "/" in a HTML page:

class DL
  var files
 
  def init()
    import path
    self.files = path.listdir("/")
    self.web_add_handler()
    tasmota.add_driver(self)
  end

  def web_add_button()
    import webserver
    webserver.content_send(
      "<form id='dl_demo' style='display: block;' action='Demo' method='get'><button>Download Demo</button></form><p></p>")
  end

  def download(args)
    import webserver
    var fpath = f"/{args}"
    var f = open(fpath,"r")
    var data = f.readbytes()
    webserver.content_open(200,"application/octet-stream")
    webserver.content_send(data)
    f.close()
  end

  #######################################################################
  # Display the complete page
  #######################################################################

  def getFileList()
    var el = "<div id='files'>"
    for file:self.files
      el += f"<a href='/b_dl?dl={file}' download='{file}'>{file}</a><br><br>"
    end
    el += "</div>"
    return el
  end

  def page_download()
    import webserver
    import json

    if !webserver.check_privileged_access() return nil end
    
    # regular web page
    webserver.content_start("Downloads")       #- title of the web page -#
    webserver.content_send_style()            #- send standard Tasmota styles -#

    webserver.content_send("<div>Berry Demo")
    webserver.content_send("<p>Download files from '/'</p></div><br>")        #- close .parent div-#
    webserver.content_send(self.getFileList())

    webserver.content_button(webserver.BUTTON_MANAGEMENT) #- button back to management page -#
    webserver.content_stop()                        #- end of web page -#
  end

  #######################################################################
  # Web Controller, called by POST to `/b_dl`
  #######################################################################
  def page_download_ctl()
    import webserver
    if !webserver.check_privileged_access() return nil end

    try
      if webserver.has_arg("dl")
        self.download(webserver.arg(0))
      else
        raise "value_error", "Unknown command"
      end
    except .. as e, m
      print(format("BRY: Exception> '%s' - %s", e, m))
      #- display error page -#
      webserver.content_start("Parameter error")      #- title of the web page -#
      webserver.content_send_style()                  #- send standard Tasmota styles -#

      webserver.content_send(format("<p style='width:340px;'><b>Exception:</b><br>'%s'<br>%s</p>", e, m))

      webserver.content_button(webserver.BUTTON_MANAGEMENT) #- button back to management page -#
      webserver.content_send("<p></p>")
      webserver.content_stop()                        #- end of web page -#
    end
  end

  def web_add_handler()
    import webserver
    #- we need to register a closure, not just a function, that captures the current instance -#
    webserver.on("/Demo", / -> self.page_download(), webserver.HTTP_GET)
    webserver.on("/b_dl", / -> self.page_download_ctl(), webserver.HTTP_GET)
  end
end

dl = DL()

@sfromis
Copy link
Contributor

sfromis commented May 23, 2024

Nice that it seems to be possible now, since #15573

@s-hadinger
Copy link
Collaborator

I read back my comment from this PR. We need to check that the underlying implementation does not convert to String anymore

@sfromis
Copy link
Contributor

sfromis commented May 24, 2024

Part of the context is also the not-really-documented #define UFILESYS_STATIC_SERVING supporting the UFSServe command to set up a web server from the file system, implying support for binary files. Not tested that....

Of course, that's not really equivalent to having Berry code able to directly serve binary data, which in some cases can be quite handy instead of needing to write such data to the file system first.

Anyway, if the webserver module can now handle binary data without going via the file system, "for completeness" it would be nice if the webclient module also could do it the other way, instead of needing to go via write_file for binary data.

@s-hadinger s-hadinger merged commit 1ca91a8 into arendst:development May 24, 2024
59 checks passed
@Staars Staars deleted the patch-6 branch July 11, 2024 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants