diff --git a/backend/pikpak/pikpak.go b/backend/pikpak/pikpak.go
index c3f82a079..81645c13c 100644
--- a/backend/pikpak/pikpak.go
+++ b/backend/pikpak/pikpak.go
@@ -1695,18 +1695,31 @@ func (o *Object) upload(ctx context.Context, in io.Reader, src fs.ObjectInfo, wi
 
 	// Calculate gcid; grabbed from package jottacloud
 	var gcid string
-	// unwrap the accounting from the input, we use wrap to put it
-	// back on after the buffering
-	var wrap accounting.WrapFn
-	in, wrap = accounting.UnWrap(in)
-	var cleanup func()
-	gcid, in, cleanup, err = readGcid(in, size, int64(o.fs.opt.HashMemoryThreshold))
-	defer cleanup()
-	if err != nil {
-		return fmt.Errorf("failed to calculate gcid: %w", err)
+	if srcObj := fs.UnWrapObjectInfo(src); srcObj != nil && srcObj.Fs().Features().IsLocal {
+		// No buffering; directly calculate gcid from source
+		rc, err := srcObj.Open(ctx)
+		if err != nil {
+			return fmt.Errorf("failed to open src: %w", err)
+		}
+		defer fs.CheckClose(rc, &err)
+
+		if gcid, err = calcGcid(rc, srcObj.Size()); err != nil {
+			return fmt.Errorf("failed to calculate gcid: %w", err)
+		}
+	} else {
+		// unwrap the accounting from the input, we use wrap to put it
+		// back on after the buffering
+		var wrap accounting.WrapFn
+		in, wrap = accounting.UnWrap(in)
+		var cleanup func()
+		gcid, in, cleanup, err = readGcid(in, size, int64(o.fs.opt.HashMemoryThreshold))
+		defer cleanup()
+		if err != nil {
+			return fmt.Errorf("failed to calculate gcid: %w", err)
+		}
+		// Wrap the accounting back onto the stream
+		in = wrap(in)
 	}
-	// Wrap the accounting back onto the stream
-	in = wrap(in)
 
 	if !withTemp {
 		info, err := o.fs.upload(ctx, in, leaf, dirID, gcid, size, options...)