Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Feat: Upload multiple files, upload directory #1

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

Merged
merged 2 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ When executed, the program reads the specified file and creates a multipart/form

Upon a successful upload, the program outputs the URL to the uploaded file to the console and also **writes it to the clipboard**.

### Uploading multiple files

When using `-dir` mode, the program will loop through all files in the directory provided and upload each one. Upon completing all uploads, the program will output a table with the files uploaded and the URL generated. This output will also be saved to a file in the source directory naned `upload.xxxxx.log`.

The same functionality is available if more than one arg is provided (e.g. `npu file1.jpg file2.jpg`) but the log file will be stored in the executable's root directory.

## To do

- [ ] Add support for multiple files
- [ ] Add support for directories
- [x] Add support for multiple files
- [x] Add support for directories
- [ ] Add support for archiving files (with encryption)
- [ ] Add configuration file

Expand Down
1 change: 1 addition & 0 deletions src/npu/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.20
require golang.design/x/clipboard v0.6.3

require (
github.com/rodaine/table v1.1.0 // indirect
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect
Expand Down
10 changes: 10 additions & 0 deletions src/npu/go.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rodaine/table v1.1.0 h1:/fUlCSdjamMY8VifdQRIu3VWZXYLY7QHFkVorS8NTr4=
github.com/rodaine/table v1.1.0/go.mod h1:Qu3q5wi1jTQD6B6HsP6szie/S4w1QUQ8pq22pz9iL8g=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.design/x/clipboard v0.6.3 h1:qIAjOL1yYLzfEclnPjaoUF4FTqmk4C57LB9MBz2QwCM=
golang.design/x/clipboard v0.6.3/go.mod h1:kqBSweBP0/im4SZGGjLrppH0D400Hnfo5WbFKSNK8N4=
Expand Down Expand Up @@ -39,3 +47,5 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
129 changes: 109 additions & 20 deletions src/npu/npu.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,34 @@ package main

import (
"bytes"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"

"github.com/rodaine/table"
"golang.design/x/clipboard"
)

type uploadedFile struct {
fileName string
upUrl string
}

func main() {
expires := flag.Int("expires", 0, "set expiration time for the uploaded file in hours")
secret := flag.Bool("secret", false, "enable secret mode")
dirMode := flag.Bool("dir", false, "enable directory mode (non-recursive)")

flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: [-expires <hours>] [-secret] <filePath>\n")
fmt.Fprintf(os.Stderr, "Usage: [-expires <hours>] [-secret] [-dir] <directory/filePath>\n")
flag.PrintDefaults()
fmt.Print("\n")
os.Exit(1)
Expand All @@ -31,21 +40,108 @@ func main() {
if len(args) < 1 {
flag.Usage()
}
filePath := args[0]

// construct list of files to upload
filePaths := []string{}
dir := "./"
if *dirMode {
dir = strings.TrimSuffix(strings.TrimSuffix(args[0], "/"), "\\") // trim trailing slashes

dirEntries, err := os.ReadDir(dir)
if err != nil {
fmt.Println("Failed to read files in directory:", err)
os.Exit(1)
}

for _, entry := range dirEntries {
if !entry.IsDir() {
filePaths = append(filePaths, fmt.Sprintf("%s/%s", dir, entry.Name()))
}
}
} else {
for _, argPath := range args {
filePaths = append(filePaths, argPath)
}
}

// upload files
uploadedFiles := []uploadedFile{}
for _, filePath := range filePaths {
responseBody, err := uploadFile(filePath, expires, secret)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

if responseBody != nil {
upUrl := string(responseBody)
upUrl = strings.TrimSuffix(strings.TrimSuffix(upUrl, "\n"), "\r")

uploadedFiles = append(uploadedFiles, uploadedFile{
fileName: filepath.Base(filePath),
upUrl: upUrl,
})
}
}

if len(uploadedFiles) == 1 {
// original, single file upload
upUrl := uploadedFiles[0].upUrl + "\n"
fmt.Println(string(upUrl))
clipboard.Write(clipboard.FmtText, []byte(upUrl))
} else {
// for multiple file upload
printTblOutput(&uploadedFiles)
logFile, err := writeOutputToFile(dir, &uploadedFiles)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

fmt.Println("\nLog file saved:", logFile)
}
}

func printTblOutput(uploadedFiles *[]uploadedFile) {
tbl := table.New("File", "| 0x0 Url")
for _, upFile := range *uploadedFiles {
tbl.AddRow(upFile.fileName, "| "+upFile.upUrl)
}
tbl.Print()
}

func writeOutputToFile(dir string, uploadedFiles *[]uploadedFile) (logFile string, err error) {
logFile = fmt.Sprintf("%s/%s.%d.log", dir, "upload", time.Now().Unix())
f, err := os.Create(logFile)
if err != nil {
return "", errors.New(fmt.Sprintf("Failed to save output log file: %v", err))
}
defer f.Close()

for _, upFile := range *uploadedFiles {
_, err = f.WriteString(fmt.Sprintf("%s: %s\r\n", upFile.fileName, upFile.upUrl))
if err != nil {
return "", errors.New(fmt.Sprintf("Failed to write to log file: %v", err))
}
}

f.Sync()

return logFile, nil
}

func uploadFile(filePath string, expires *int, secret *bool) (responseBody []byte, err error) {
file, err := os.Open(filePath)
if err != nil {
fmt.Println("Failed to open file:", err)
os.Exit(1)
return nil, errors.New(fmt.Sprintf("Failed to open file: %v", err))
}
defer file.Close()

body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("file", filepath.Base(filePath))
if err != nil {
fmt.Println("Failed to create form file:", err)
os.Exit(1)
return nil, errors.New(fmt.Sprintf("Failed to create form file: %v", err))
}
io.Copy(part, file)

Expand All @@ -62,33 +158,26 @@ func main() {

err = writer.Close()
if err != nil {
fmt.Println("Failed to close multipart writer:", err)
os.Exit(1)
return nil, errors.New(fmt.Sprintf("Failed to close multipart writer: %v", err))
}

request, err := http.NewRequest("POST", "https://0x0.st", body)
if err != nil {
fmt.Println("Failed to create HTTP request:", err)
os.Exit(1)
return nil, errors.New(fmt.Sprintf("Failed to create HTTP request: %v", err))
}
request.Header.Set("Content-Type", writer.FormDataContentType())

client := &http.Client{}
response, err := client.Do(request)
if err != nil {
fmt.Println("Failed to send HTTP request:", err)
os.Exit(1)
return nil, errors.New(fmt.Sprintf("Failed to send HTTP request: %v", err))
}
defer response.Body.Close()

responseBody, err := ioutil.ReadAll(response.Body)
responseBody, err = io.ReadAll(response.Body)
if err != nil {
fmt.Println("Failed to read response body:", err)
os.Exit(1)
return nil, errors.New(fmt.Sprintf("Failed to read response body: %v", err))
}

if responseBody != nil {
fmt.Println(string(responseBody))
clipboard.Write(clipboard.FmtText, []byte(string(responseBody)))
}
return responseBody, nil
}