Reputation: 1800
I am trying to build a small app that allows you to upload large files in chunks. I will add the ability to pause later as well.
I am stuck on this error inside my loop: Error processing file:multipart: NextPart: EOF
func main() {
router := gin.Default()
rg := router.Group("api/v1")
{
rg.POST("/photo", uploadFile)
}
router.Use(CORSMiddleware())
router.Run()
}
func uploadFile(c *gin.Context) {
mr, e := c.Request.MultipartReader()
if e != nil {
panic("Error reading request:" + e.Error())
}
client, e := storage.NewClient(c, option.WithAPIKey(uploadApiKey))
bucket := client.Bucket(uploadBucket)
for {
p, e := mr.NextPart()
if e == io.EOF {
break
} else if e != nil {
panic("Error processing file:" + e.Error())
}
w := bucket.Object(p.FileName()).NewWriter(c)
if _, e := io.Copy(w, p); e != nil {
panic("Error during chunk upload:" + e.Error())
} else if e := w.Close(); e != nil {
panic("Could not finalize chunk writing:" + e.Error())
}
}
}
The Client-side code looks like this:
class FileToUpload {
static chunkSize = 512000;
static uploadUrl = 'http://localhost:8080/api/v1/photo';
readonly request: XMLHttpRequest;
readonly file: File;
readonly name: string;
currentChunkStartByte: number;
currentChunkFinalByte: number;
constructor(file: File, name: string) {
this.request = new XMLHttpRequest();
this.file = file;
this.name = name;
this.currentChunkStartByte = 0;
this.currentChunkFinalByte = FileToUpload.chunkSize > this.file.size ? this.file.size : FileToUpload.chunkSize;
}
uploadFile() {
let chunk: Blob = this.file.slice(this.currentChunkStartByte, this.currentChunkFinalByte);
this.request.overrideMimeType('application/octet-stream');
this.request.open('POST', FileToUpload.uploadUrl, true);
const randomNum = Math.random().toString().substr(2);
this.request.setRequestHeader('Content-Type', 'multipart/form-data; boundary=--'+randomNum);
this.request.setRequestHeader('Content-Range', `bytes ${this.currentChunkStartByte}-${this.currentChunkFinalByte}/${this.file.size}`);
this.request.onload = () => {
if(this.currentChunkFinalByte === this.file.size) {
// Do something once done with file
return;
}
this.currentChunkStartByte = this.currentChunkFinalByte;
this.currentChunkFinalByte = this.currentChunkStartByte + FileToUpload.chunkSize;
this.uploadFile();
}
this.request.send(chunk);
}
}
I already have a check for EOF, I don't understand why I still get this error. Any ideas?
Upvotes: 4
Views: 16102
Reputation: 393
According to the source code such an error that would consist io.EOF
can be returned only in case if closing boundary wasn't found in the stream body however the client marked body as sent. In your case either boundary is missing in the request body that would mark the end of the file content or you're not parsing it on the server side.
Source code: https://golang.org/src/mime/multipart/multipart.go#L339
In your specific case, http.Request
's MultipartReader()
would parse your boundary beforehand so you don't need to do anything additional there (https://golang.org/src/net/http/request.go#L486). At the same time I don't see any code that would append file boundary to the stream on the client side.
Unfortunately the code that is responsible for writing the content into the body is not presented here therefore I can't hint a solution here, but I'm pretty sure that randomly generated boundary on the client side is not passed to anywhere else besides the Content-Type
header which is the main reason of the issue you're facing.
Please read more information about how multipart forms work in particular in this answer: https://stackoverflow.com/a/8660740/8008395
Upvotes: 5