--- /dev/null
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookStackConsole", "BookStackConsole\BookStackConsole.csproj", "{BA24248C-5A62-427B-8D67-5CB3386FF3B8}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BA24248C-5A62-427B-8D67-5CB3386FF3B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BA24248C-5A62-427B-8D67-5CB3386FF3B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BA24248C-5A62-427B-8D67-5CB3386FF3B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BA24248C-5A62-427B-8D67-5CB3386FF3B8}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
--- /dev/null
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Http;
+
+namespace BookStackConsole
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ // Check expected command arguments have been passed
+ if (args.Length < 2)
+ {
+ Console.Error.WriteLine("Both <page_id> and <file_path> need to be provided!");
+ Environment.Exit(1);
+ }
+
+ // Get our BookStack details from the environment
+ var baseUrl = Environment.GetEnvironmentVariable("BS_URL") ?? "";
+ var tokenId = Environment.GetEnvironmentVariable("BS_TOKEN_ID") ?? "";
+ var tokenSecret = Environment.GetEnvironmentVariable("BS_TOKEN_SECRET") ?? "";
+ baseUrl = baseUrl.TrimEnd('/');
+ Console.WriteLine("base: " + baseUrl);
+
+ // Get our target page ID and file path from command args.
+ var pageId = args[0];
+ var filePath = args[1];
+
+ // Check our file exists
+ if (!File.Exists(filePath))
+ {
+ Console.Error.WriteLine("Both <page_id> and <file_path> need to be provided!");
+ Environment.Exit(1);
+ }
+
+ // Get our file name and read stream
+ var fileName = Path.GetFileName(filePath);
+ var fileStream = File.OpenRead(filePath);
+
+ // Format our post data
+ var postData = new MultipartFormDataContent();
+ postData.Add(new StringContent(pageId), "uploaded_to");
+ postData.Add(new StringContent(fileName), "name");
+ postData.Add(new StreamContent(fileStream), "file", fileName);
+
+ // Attempt to send up our file
+ var client = new HttpClient();
+ client.DefaultRequestHeaders.Add("Authorization", $"Token {tokenId}:{tokenSecret}");
+ var respMessage = client.PostAsync(baseUrl + "/api/attachments", postData);
+
+ // Write out a message to show success/failure along with response data
+ Console.WriteLine("Response: " + respMessage.Result.Content.ReadAsStringAsync().Result);
+ if (respMessage.IsCompletedSuccessfully && respMessage.Result.StatusCode == HttpStatusCode.OK)
+ {
+ Console.WriteLine("Attachment uploaded successfully!");
+ Environment.Exit(0);
+ }
+
+ Console.WriteLine("Attachment failed to upload!");
+ Environment.Exit(1);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+# Upload a file attachment to a BookStack page
+
+This project will produce an executable "BookStackConsole" binary
+that takes a path to any local file and attempt
+to upload it to a BookStack page as an attachment
+using the API using a multipart/form-data request.
+
+**This is very simplistic and has been written with very little c#/.net knowledge, it is only mean to serve as a working example.**
+
+## Requirements
+
+You will need .NET installed (Tested on .NET 5.0 on Fedora 35 Linux).
+
+## Running
+
+First, download all the files in the same directory as this readme to a folder on your system
+and run the below from within that directory.
+
+```bash
+# Setup
+# ALTERNATIVELY: Open the program.cs file and add to the empty strings in the variables near the top.
+export BS_URL=https://bookstack.example.com # Set to be your BookStack base URL
+export BS_TOKEN_ID=abc123 # Set to be your API token_id
+export BS_TOKEN_SECRET=123abc # Set to be your API token_secret
+
+# Build with dotnet
+dotnet build
+
+# Running the script
+./BookStackConsole/bin/Debug/net5.0/BookStackConsole <page_id> <file_path>
+```
+
+- `<page_id>` - The ID of the page you want to upload the attachment to.
+- `<file_path>` - File you want to upload as an attachment.
+
+## Examples
+
+```bash
+# Upload the 'cat-image-collection.zip' file as an attachment to page of ID 205
+./BookStackConsole/bin/Debug/net5.0/BookStackConsole 205 ./cat-image-collection.zip
+```
\ No newline at end of file