upload.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package upload
  2. import (
  3. "errors"
  4. "io"
  5. "log"
  6. "net/http"
  7. "os"
  8. "path/filepath"
  9. user "imuslab.com/arozos/mod/user"
  10. )
  11. type chunk struct {
  12. PartFilename string
  13. DestFilename string
  14. }
  15. /*
  16. //Usage:
  17. uploadedFilepaths, err := upload.StreamUploadToDisk(userinfo, w, r)
  18. if err != nil {
  19. sendErrorResponse(w, err.Error())
  20. return
  21. }
  22. //Set the ownership of file(s)
  23. quotaFulled := false
  24. for _, fileRealpath := range uploadedFilepaths {
  25. //Check for storage quota
  26. uploadFileSize := fs.GetFileSize(fileRealpath)
  27. if !userinfo.StorageQuota.HaveSpace(uploadFileSize) {
  28. //User storage quota fulled. Remove this file
  29. quotaFulled = true
  30. //Remove this file as this doesn't fit
  31. os.Remove(fileRealpath)
  32. } else {
  33. userinfo.SetOwnerOfFile(fileRealpath)
  34. }
  35. }
  36. if quotaFulled {
  37. sendErrorResponse(w, "Storage Quota Full")
  38. return
  39. }
  40. sendOK(w)
  41. return
  42. */
  43. func StreamUploadToDisk(userinfo *user.User, w http.ResponseWriter, r *http.Request) ([]string, error) {
  44. //Check if this userinfo is valid
  45. if userinfo == nil {
  46. return []string{}, errors.New("Invalid userinfo")
  47. }
  48. vpath, ok := r.URL.Query()["path"]
  49. if !ok || len(vpath) == 0 {
  50. return []string{}, errors.New("Invalid upload destination")
  51. }
  52. //Get the upload destination realpath
  53. realUploadPath, err := userinfo.VirtualPathToRealPath(vpath[0])
  54. if err != nil {
  55. //Return virtual path to real path translation error
  56. return []string{}, err
  57. }
  58. //Try to parse the FORM POST using multipart reader
  59. reader, err := r.MultipartReader()
  60. if err != nil {
  61. log.Println("Upload failed: " + err.Error())
  62. return []string{}, err
  63. }
  64. //Start write process
  65. uplaodedFiles := map[string]string{}
  66. for {
  67. part, err := reader.NextPart()
  68. if err == io.EOF {
  69. break
  70. } else if err != nil {
  71. //Connection lost when uploading. Remove the uploading file.
  72. clearFailedUploadChunks(uplaodedFiles)
  73. return []string{}, errors.New("Upload failed")
  74. }
  75. defer part.Close()
  76. //Check if this is file or other paramters
  77. if part.FileName() != "" {
  78. //This part is a part of a file. Write it to destination folder
  79. tmpFilepath := filepath.Join(realUploadPath, part.FileName()+".tmp")
  80. //Check if this part is uploaded before. If not but the .tmp file exists
  81. //This is from previous unsuccessful upload and it should be reoved
  82. _, ok := uplaodedFiles[part.FileName()]
  83. if !ok && fileExists(tmpFilepath) {
  84. //This chunk is first chunk of the file and the .tmp file already exists.
  85. //Remove it
  86. log.Println("Removing previous failed upload: ", tmpFilepath)
  87. os.Remove(tmpFilepath)
  88. }
  89. //Check if the uploading target folder exists. If not, create it
  90. if !fileExists(filepath.Dir(tmpFilepath)) {
  91. os.MkdirAll(filepath.Dir(tmpFilepath), 0755)
  92. }
  93. //Open the file and write to it using append mode
  94. d, err := os.OpenFile(tmpFilepath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0775)
  95. if err != nil {
  96. //Failed to create new file.
  97. clearFailedUploadChunks(uplaodedFiles)
  98. return []string{}, errors.New("Write to disk failed")
  99. }
  100. io.Copy(d, part)
  101. d.Close()
  102. //Record this file
  103. uplaodedFiles[part.FileName()] = tmpFilepath
  104. } else {
  105. //Unknown stuffs
  106. continue
  107. }
  108. }
  109. //Remove the .tmp extension for the file.
  110. uploadedFilepaths := []string{}
  111. for thisFilename, thisFilepath := range uplaodedFiles {
  112. thisFilename = filepath.Base(thisFilename)
  113. finalDestFilename := filepath.Join(filepath.Dir(thisFilepath), thisFilename)
  114. //Remove the .tmp from the upload by renaming it as the original name
  115. err = os.Rename(thisFilepath, finalDestFilename)
  116. if err != nil {
  117. clearFailedUploadChunks(uplaodedFiles)
  118. return []string{}, err
  119. }
  120. uploadedFilepaths = append(uploadedFilepaths, finalDestFilename)
  121. log.Println(userinfo.Username+" uploaded: ", filepath.Base(thisFilepath))
  122. }
  123. return uploadedFilepaths, nil
  124. }
  125. //This function remove all the chunks for failed file upload from disk
  126. func clearFailedUploadChunks(uplaodedFiles map[string]string) {
  127. for _, tmpFiles := range uplaodedFiles {
  128. os.Remove(tmpFiles)
  129. }
  130. }