Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating a File Upload Bot

0.00/5 (No votes)
14 Feb 2022 1  
How to create a Spring Boot web app in Java that replicates the functionality of the “Teams File Upload” sample app on GitHub
This is Part 3 of a 3-part series that demonstrates how to build Microsoft Teams apps in Java, with a focus on bots. This article shows how to create a bot that downloads and processes file attachments in chat messages.

Chatbots provide a convenient platform when working with file upload. Chat platforms like Microsoft Teams already expose a familiar interface for attaching files to messages, as well as taking care of hosting any message attachments. This relieves us of having to code the rather mundane, but still tricky logic that would otherwise be required to handle file uploads ourselves.

In this post, we’ll create a bot that downloads and processes file attachments in chat messages.

Prerequisites

To follow along in this article, you’ll need a Java 11 JDK, Apache Maven, Node.js and npm, an Azure subscription to deploy the final application, and the Azure CLI.

I’ve also used a scaffolding tool, Yeoman, to simplify setup. Install it with the following command:

npm install -g yo

The chatbot template is provided by the generator-botbuilder-java package, which you’ll need to install with this command:

npm install -g generator-botbuilder-java

You now have everything you need to create your sample chatbot application.

Example Source Code

You can follow along by examining this project’s source code on its GitHub page.

Build the Base Application

Refer to the previous article in this series for the process of building and updating the base application. Follow the instructions under the headings “Creating the Sample Application” and “Updating the Sample Application” to create a Spring Boot project using Yeoman.

Add New Dependencies

In addition to the updated dependencies mentioned in the previous article, you also need to add Maven dependencies to support your file upload bot.

Add the following dependencies to the pom.xml file:

XML
<dependencies>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.11.0</version>
    </dependency></dependencies>

Build the Upload Bot

Your bot is defined in the UploadBot class and extends the ActivityHandler class:

Java
public class UploadBot extends ActivityHandler {

The onMembersAdded method displays a message to any new chat members informing them that the bot will download files from message attachments:

Java
@Override
protected CompletableFuture<Void> onMembersAdded(
    final List<ChannelAccount> membersAdded,
    final TurnContext turnContext
) {
  return membersAdded.stream()
      .filter(
          member -> !StringUtils
              .equals(member.getId(), turnContext.getActivity().getRecipient().getId())
      ).map(channel -> turnContext.sendActivity(
          MessageFactory.text
          ("Welcome! Post a message with an attachment and I'll download it!")))
      .collect(CompletableFutures.toFutureList()).thenApply(resourceResponses -> null);
}

The onMessageActivity method inspects any new messages to determine if they have attachments:

Java
@Override
protected CompletableFuture<Void> onMessageActivity(final TurnContext turnContext) {
  if (messageWithDownload(turnContext.getActivity())) {
    final Attachment attachment = turnContext.getActivity().getAttachments().get(0);

If an attachment was found, the file is downloaded to a local temporary location:

Java
return downloadAttachment(attachment)

If there were any issues with the file download, a message is posted to the chat:

Java
.thenCompose(result -> !result.result()
    ? turnContext.sendActivityBlind(
    MessageFactory.text("Failed to download the attachment"))

If the attachment was successfully downloaded, it is inspected, and the file length and type are posted to the chat:

Java
          : turnContext.sendActivityBlind(
              MessageFactory.text(
                  "Downloaded file " + attachment.getName() + ". It was "
                      + getFileSize(result.getRight())
                      + " bytes long and appears to be of type "
                      + getFileType(result.getRight())))
      );
}

If no attachments were found, the bot posts a reminder to the chat informing users that it will download any attachments:

Java
  return turnContext.sendActivity(
      MessageFactory.text("Post a message with an attachment and I'll download it!")
  ).thenApply(sendResult -> null);
}

To determine if a message has an attachment, the messageWithDownload method checks the attachments collection and verifies the content type of the first attachment:

Java
private boolean messageWithDownload(final Activity activity) {
  return activity.getAttachments() != null
      && activity.getAttachments().size() > 0
      && StringUtils.equalsIgnoreCase(
      activity.getAttachments().get(0).getContentType(),
      FileDownloadInfo.CONTENT_TYPE);
}

Downloading an attachment is similar to downloading any file from an HTTP server. The downloadAttachment method downloads attachments to a temporary location:

Java
private CompletableFuture<ResultPair<String>> downloadAttachment(final Attachment attachment) {

Any shared variable accessed from within lambda methods must be final, or effectively final, which simply means their value does not change once it has been set.

So, the result of your file download is captured in a ResultPair, which is wrapped in a final AtomicReference. This allows you to return the results of your file download from within the lambda methods created next:

Java
final AtomicReference<ResultPair<String>> result = new AtomicReference<>();

The app creates a temporary file, then uses the Apache Commons library to download the attachment to it:

Java
return CompletableFuture.runAsync(() -> {
      try {
        final FileDownloadInfo fileDownload = Serialization
            .getAs(attachment.getContent(), FileDownloadInfo.class);
        final File filePath = Files.createTempFile(
            FilenameUtils.getBaseName(attachment.getName()),
            "." + FilenameUtils.getExtension(attachment.getName())).toFile();

        FileUtils.copyURLToFile(
            new URL(fileDownload.getDownloadUrl()),
            filePath,
            30000,
            30000);

If everything went well, you return true and the path to the temporary file by setting the value wrapped by the AtomicReference:

Java
  result.set(new ResultPair<>(true, filePath.getAbsolutePath()));
} catch (Throwable t) {

In the event of an error, you return false and the error message:

Java
    result.set(new ResultPair<>(false, t.getLocalizedMessage()));
  }
})

The wrapped ResultPair is then returned by the CompletableFuture:

Java
      .thenApply(aVoid -> result.get());
}

To demonstrate that the bot has successfully downloaded the attachment, the getFileSize method returns the file’s size:

Java
private long getFileSize(final String path) {
  try {
    return Files.size(Paths.get(path));
  } catch (IOException e) {
    return -1;
  }
}

You also inspect the file’s type with the getFileType method:

Java
  private String getFileType(final String path) {
    try {
      final String type = Files.probeContentType(Paths.get(path));
      return type == null ? "unknown" : type;
    } catch (IOException e) {
      return "unknown";
    }
  }
}

Test the Bot

Refer to the instructions in the first article in this series, in the “Deploy the Bot” and “Link to Teams” sections, to deploy the catering bot and integrate it with Teams.

Then, post a message with an attachment. The bot will detect the attachment, download it, and report back with the file size and type:

Title: Inserting image...

Conclusion

Handling files with a chatbot is straightforward, requiring only the ability to download HTTP files from regular attachments in messages.

In this article, you created a simple chatbot that downloaded any message attachments and reported back the file size to indicate that it has successfully downloaded the attachment.

This example is easy to extend with any additional logic that your teams may require, allowing users to upload files through the familiar Teams chat interface. It can be used as the basis for tools such as file conversion, and can support workflows or application deployments. Simply clone the source code from GitHub and add your own custom logic to the UploadBot class to build a bot that suits your needs.

To learn more about building your own bot for Microsoft Teams, check out Building great bots for Microsoft Teams with Azure Bot Framework Composer.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here