// Proto to domain public static FileItem Convert(this gFileItem file) { if (file == null) { return(new FileItem()); } var status = file.Status switch { Protos.ItemStatus.Visible => Domain.ItemStatus.Visible, Protos.ItemStatus.Hidden => Domain.ItemStatus.Hidden, Protos.ItemStatus.ToBeProcessed => Domain.ItemStatus.To_Be_Processed, Protos.ItemStatus.ToBeDeleted => Domain.ItemStatus.To_Be_Deleted, _ => Domain.ItemStatus.Visible, }; return(new FileItem() { Id = file.Id, TusId = file.TusId, UserId = file.UserId, Size = file.Size, Name = file.Name, MimeType = file.MimeType, FolderId = file.FolderId, Extension = file.Extension, CreatedAt = file.CreatedAt.ToDateTime(), LastModified = file.LastModified.ToDateTime(), Status = status, StorageServerId = Guid.Parse(file.StorageServerId), }); }
/// <summary> /// Persist the uploaded file data to db /// </summary> private async Task <gFileItem> PersistMetaData(FileCompleteContext ctx) { var gUserResponse = await _fileMngClient.GetUserAsync(new gUserRequest()); if (gUserResponse == null) { _logger.LogError("Unknown error occured while requesting user info"); ctx.HttpContext.Response.StatusCode = 400; return(null); } if (gUserResponse.Status.Status != Protos.RequestStatus.Success) { _logger.LogError(gUserResponse.Status.Message); ctx.HttpContext.Response.StatusCode = 400; return(null); } ITusFile file = await((ITusReadableStore)ctx.Store).GetFileAsync(ctx.FileId, ctx.CancellationToken); var metadata = await file.GetMetadataAsync(ctx.CancellationToken); gFileItem fileitem = new gFileItem() { Id = Helpers.GenerateUniqueId(), TusId = ctx.FileId, UserId = gUserResponse.User.Id, Size = uint.Parse(new FileInfo(Path.Combine(_uploadOpts.CurrentValue.UploadPath, file.Id)).Length.ToString()), Name = metadata["name"].GetString(Encoding.UTF8), MimeType = metadata["contentType"].GetString(Encoding.UTF8), // file with no folderid is placed in the virtual root folder FolderId = metadata["folderId"].GetString(Encoding.UTF8) == "root" ? null : metadata["folderId"].GetString(Encoding.UTF8), Extension = Helpers.GetFileExtension(metadata["contentType"].GetString(Encoding.UTF8)) ?? string.Empty, StorageServerId = metadata["serverId"].GetString(Encoding.UTF8), CreatedAt = Timestamp.FromDateTime(DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc)), LastModified = Timestamp.FromDateTime(DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc)), Status = Protos.ItemStatus.Visible }; // send the uploaded file info to the main app gFileItemResponse response = await _fileMngClient.SaveFileAsync(new gFileItemRequest() { FileItem = fileitem }); if (response == null) { _logger.LogError("No response has been received from the server.", ErrorOrigin.Server); return(null); } if (response.Status.Status != Protos.RequestStatus.Success) { _logger.LogError("An error has been returned from server call: " + response.Status.Message); return(null); } return(response.FileItem); }
/// <summary> /// Get the response header made by the current http request. /// download request can be resumed, so correct header must be set /// </summary> /// <param name="httpRequest"></param> /// <param name="fileInfo"></param> /// <returns></returns> private HttpResponseHeader GetResponseHeader(gFileItem file) { if (_httpContext.Request == null || file == null) { return(null); } long startPosition = 0; string contentRange = ""; string fileName = file.Name; long fileLength = file.Size; string lastUpdateTimeStr = file.LastModified.ToString(); string eTag = HttpUtility.UrlEncode(fileName, Encoding.UTF8) + " " + lastUpdateTimeStr; string contentDisposition = "attachment;filename=" + HttpUtility.UrlEncode(fileName, Encoding.UTF8).Replace("+", "%20"); if (_httpContext.Request.Headers["Range"] != StringValues.Empty) { string[] range = _httpContext.Request.Headers["Range"].ToString().Split(new char[] { '=', '-' }); startPosition = Convert.ToInt64(range[1]); if (startPosition < 0 || startPosition >= fileLength) { return(null); } } if (_httpContext.Request.Headers["If-Range"].ToString() != null) { if (_httpContext.Request.Headers["If-Range"].ToString().Replace("\"", "") != eTag) { startPosition = 0; } } string contentLength = (fileLength - startPosition).ToString(); if (startPosition > 0) { contentRange = string.Format(" bytes {0}-{1}/{2}", startPosition, fileLength - 1, fileLength); } HttpResponseHeader responseHeader = new HttpResponseHeader { AcceptRanges = "bytes", Connection = "Keep-Alive", ContentDisposition = contentDisposition, ContentEncoding = Encoding.UTF8, ContentLength = contentLength, ContentRange = contentRange, ContentType = "application/octet-stream", Etag = eTag, LastModified = lastUpdateTimeStr }; return(responseHeader); }
/// <summary> /// Moves the uploaded files (the actual file and it's metadata) to the user folder /// tus protocol puts the uploaded files into the store, XtraUpload move those files to the user directory /// </summary> private void MoveFilesToFolder(FileCompleteContext ctx, gFileItem fileItem) { string userFolder = Path.Combine(_uploadOpts.CurrentValue.UploadPath, fileItem.UserId); string destFolder = Path.Combine(userFolder, fileItem.Id); string newFileFullPath = Path.Combine(destFolder, fileItem.Id); // Create user root directory if (!Directory.Exists(userFolder)) { Directory.CreateDirectory(userFolder); } // Create a new directory inside the user root dir if (!Directory.Exists(destFolder)) { Directory.CreateDirectory(destFolder); } // move all files to the destination folder DirectoryInfo directoryInfo = new DirectoryInfo(_uploadOpts.CurrentValue.UploadPath); foreach (FileInfo file in directoryInfo.GetFiles(ctx.FileId + "*")) { // Exemple of file names generated by tus are (...69375.metadata, ...69375.uploadlength ...) string[] subNames = file.Name.Split('.'); string subName = subNames.Count() == 2 ? '.' + subNames[1] : string.Empty; File.Move(file.FullName, newFileFullPath + subName); } // Create thumbnails for img file (less than 15mb) if (fileItem.MimeType.StartsWith("image") && fileItem.Size < (1024L * 1024L * 15)) { // Todo: move the process of cropping images to a job using FileStream smallThumboutStream = new FileStream(newFileFullPath + ".smallthumb.png", FileMode.Create); using Image image = Image.Load(File.ReadAllBytes(newFileFullPath), out IImageFormat format); if (image.Width >= 800 || image.Height >= 800) { int width = 960, height = 640; int aspectRatio = image.Width / image.Height; if (aspectRatio == 0) { height = 960; width = 640; } using FileStream mediumThumboutStream = new FileStream(newFileFullPath + ".mediumthumb.png", FileMode.Create); Image mediumthumbnail = image.Clone(i => i.Resize(width, height).Crop(new Rectangle(0, 0, width, height))); mediumthumbnail.Save(mediumThumboutStream, format); } Image smallthumbnail = image.Clone(i => i.Resize(128, 128).Crop(new Rectangle(0, 0, 128, 128))); smallthumbnail.Save(smallThumboutStream, format); } }
/// <summary> /// Handle the upload completion /// </summary> protected override async Task OnUploadCompleted(FileCompleteContext ctx) { try { gFileItem file = await PersistMetaData(ctx); if (file != null) { MoveFilesToFolder(ctx, file); // Attach file info to header, because tus send 204 (no response body is allowed) ctx.HttpContext.Response.Headers.Add("upload-data", Helpers.JsonSerialize(file)); } } catch (Exception _ex) { _logger.LogError(_ex.Message); throw _ex; } }