Skip to content

Commit

Permalink
Merge pull request #108 from microsoftgraph/large-file-upload
Browse files Browse the repository at this point in the history
Large file upload
  • Loading branch information
jasonjoh authored Aug 26, 2024
2 parents 538b12e + f398ef2 commit 7d4bbf5
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 12 deletions.
1 change: 1 addition & 0 deletions src/.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ GRAPH_USER_SCOPES=user.read,mail.readwrite,calendars.readwrite,group.read.all,te
ENABLE_GRAPH_LOG=false
GRAPH_LOG_TOKENS=false
GRAPH_LOG_PAYLOADS=false
LARGE_FILE_PATH=path-to-large-file
8 changes: 4 additions & 4 deletions src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ require (
github.com/jasonjoh/msgraph-sdk-go-debug-logger v0.0.2
github.com/joho/godotenv v1.5.1
github.com/microsoft/kiota-abstractions-go v1.6.1
github.com/microsoft/kiota-authentication-azure-go v1.1.0
github.com/microsoft/kiota-http-go v1.4.4
github.com/microsoftgraph/msgraph-sdk-go v1.47.0
github.com/microsoftgraph/msgraph-sdk-go-core v1.2.1
Expand All @@ -27,6 +26,7 @@ require (
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/microsoft/kiota-authentication-azure-go v1.1.0 // indirect
github.com/microsoft/kiota-serialization-form-go v1.0.0 // indirect
github.com/microsoft/kiota-serialization-json-go v1.0.8 // indirect
github.com/microsoft/kiota-serialization-multipart-go v1.0.0 // indirect
Expand All @@ -35,9 +35,9 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/std-uritemplate/std-uritemplate/go v1.0.5 // indirect
github.com/stretchr/testify v1.9.0 // indirect
go.opentelemetry.io/otel v1.28.0 // indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect
go.opentelemetry.io/otel v1.29.0 // indirect
go.opentelemetry.io/otel/metric v1.29.0 // indirect
go.opentelemetry.io/otel/trace v1.29.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sys v0.24.0 // indirect
Expand Down
12 changes: 6 additions & 6 deletions src/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/thlib/go-timezone-local v0.0.3 h1:ie5XtZWG5lQ4+1MtC5KZ/FeWlOKzW2nPoUnXYUbV/1s=
github.com/thlib/go-timezone-local v0.0.3/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI=
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
Expand Down
2 changes: 1 addition & 1 deletion src/graphhelper/graphhelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import (

"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
graphdebug "github.com/jasonjoh/msgraph-sdk-go-debug-logger"
auth "github.com/microsoft/kiota-authentication-azure-go"
khttp "github.com/microsoft/kiota-http-go"
graph "github.com/microsoftgraph/msgraph-sdk-go"
graphcore "github.com/microsoftgraph/msgraph-sdk-go-core"
auth "github.com/microsoftgraph/msgraph-sdk-go-core/authentication"
)

func NewUserGraphServiceClient(logger *log.Logger) (*graph.GraphServiceClient, error) {
Expand Down
6 changes: 5 additions & 1 deletion src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ func main() {
fmt.Println("0. Exit")
fmt.Println("1. Run batch samples")
fmt.Println("2. Run request samples")
fmt.Println("3. Run paging samples")
fmt.Println("3. Run upload samples")
fmt.Println("4. Run paging samples")

_, err = fmt.Scanf("%d", &choice)
if err != nil {
Expand All @@ -61,6 +62,9 @@ func main() {
case 2:
snippets.RunRequestSamples(userClient)
case 3:
largeFile := os.Getenv("LARGE_FILE_PATH")
snippets.RunUploadSamples(userClient, largeFile)
case 4:
snippets.RunPagingSamples(userClient)
default:
fmt.Println("Invalid choice! Please try again.")
Expand Down
135 changes: 135 additions & 0 deletions src/snippets/large_file_upload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

package snippets

import (
"context"
"fmt"
"os"
"path/filepath"

graph "github.com/microsoftgraph/msgraph-sdk-go"
"github.com/microsoftgraph/msgraph-sdk-go-core/fileuploader"
"github.com/microsoftgraph/msgraph-sdk-go/drives"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/microsoftgraph/msgraph-sdk-go/users"
)

func RunUploadSamples(graphClient *graph.GraphServiceClient, largeFile string) {
itemPath := "Documents/vacation.gif"

UploadFileToOneDrive(graphClient, largeFile, itemPath)
UploadAttachmentToMessage(graphClient, largeFile)
}

func UploadFileToOneDrive(graphClient *graph.GraphServiceClient, largeFile string, itemPath string) {
// <LargeFileUploadSnippet>
byteStream, _ := os.Open(largeFile)

// Use properties to specify the conflict behavior
itemUploadProperties := models.NewDriveItemUploadableProperties()
itemUploadProperties.SetAdditionalData(map[string]any{"@microsoft.graph.conflictBehavior": "replace"})
uploadSessionRequestBody := drives.NewItemItemsItemCreateUploadSessionPostRequestBody()
uploadSessionRequestBody.SetItem(itemUploadProperties)

// Create the upload session
// itemPath does not need to be a path to an existing item
myDrive, _ := graphClient.Me().Drive().Get(context.Background(), nil)

uploadSession, _ := graphClient.Drives().
ByDriveId(*myDrive.GetId()).
Items().
ByDriveItemId("root:/"+itemPath+":").
CreateUploadSession().
Post(context.Background(), uploadSessionRequestBody, nil)

// Max slice size must be a multiple of 320 KiB
maxSliceSize := int64(320 * 1024)
fileUploadTask := fileuploader.NewLargeFileUploadTask[models.DriveItemable](
graphClient.RequestAdapter,
uploadSession,
byteStream,
maxSliceSize,
models.CreateDriveItemFromDiscriminatorValue,
nil)

// Create a callback that is invoked after each slice is uploaded
progress := func(progress int64, total int64) {
fmt.Printf("Uploaded %d of %d bytes\n", progress, total)
}

// Upload the file
uploadResult := fileUploadTask.Upload(progress)

if uploadResult.GetUploadSucceeded() {
fmt.Printf("Upload complete, item ID: %s\n", *uploadResult.GetItemResponse().GetId())
} else {
fmt.Print("Upload failed.\n")
}
// </LargeFileUploadSnippet>
}

func ResumeUpload(
fileUploadTask fileuploader.LargeFileUploadTask[models.DriveItemable],
progress fileuploader.ProgressCallBack) {
// <ResumeSnippet>
fileUploadTask.Resume(progress)
// </ResumeSnippet>
}

func UploadAttachmentToMessage(graphClient *graph.GraphServiceClient, largeFile string) {
// <UploadAttachmentSnippet>
// Create message
message := models.NewMessage()
subject := "Large attachment"
message.SetSubject(&subject)

savedDraft, _ := graphClient.Me().Messages().Post(context.Background(), message, nil)

// Set up the attachment
byteStream, _ := os.Open(largeFile)
largeAttachment := models.NewAttachmentItem()
attachmentType := models.FILE_ATTACHMENTTYPE
largeAttachment.SetAttachmentType(&attachmentType)
fileName := filepath.Base(largeFile)
largeAttachment.SetName(&fileName)
fileInfo, _ := byteStream.Stat()
fileSize := fileInfo.Size()
largeAttachment.SetSize(&fileSize)

uploadSessionRequestBody := users.NewItemMessagesItemAttachmentsCreateUploadSessionPostRequestBody()
uploadSessionRequestBody.SetAttachmentItem(largeAttachment)

uploadSession, _ := graphClient.Me().
Messages().
ByMessageId(*savedDraft.GetId()).
Attachments().
CreateUploadSession().
Post(context.Background(), uploadSessionRequestBody, nil)

// Max slice size must be a multiple of 320 KiB
maxSliceSize := int64(320 * 1024)
fileUploadTask := fileuploader.NewLargeFileUploadTask[models.FileAttachmentable](
graphClient.RequestAdapter,
uploadSession,
byteStream,
maxSliceSize,
models.CreateFileAttachmentFromDiscriminatorValue,
nil)

// Create a callback that is invoked after each slice is uploaded
progress := func(progress int64, total int64) {
fmt.Printf("Uploaded %d of %d bytes\n", progress, total)
}

// Upload the file
uploadResult := fileUploadTask.Upload(progress)

if uploadResult.GetUploadSucceeded() {
fmt.Print("Upload complete\n")
} else {
fmt.Print("Upload failed.\n")
}
// </UploadAttachmentSnippet>
}

0 comments on commit 7d4bbf5

Please sign in to comment.