public override Task OnConnected() { var userId = AccountUtils.GetUserId(Context.User.Identity); Groups.Add(Context.ConnectionId, KeyUtils.IntToKey(userId)); return(base.OnConnected()); }
private async Task InternalPutTextIntoBlob(string text, string containerName) { if (text != null) { using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(text))) { var blobName = KeyUtils.IntToKey(this.GetUserId()); await AzureStorageUtils.UploadBlobAsync(stream, containerName, blobName, "text/plain; charset=utf-8"); } } }
private async Task InternalSaveTranscript(int resourceId, string transcript) { var entity = new GameCopycatEntity2 { PartitionKey = KeyUtils.IntToKey(resourceId), RowKey = KeyUtils.DateTimeToDescKey(DateTime.UtcNow), Transcript = transcript, UserId = this.GetUserId(), UserDisplayName = this.GetUserDisplayName(), }; await AzureStorageUtils.InsertEntityAsync(AzureStorageUtils.TableNames.GameCopycat, entity); }
public async Task <IHttpActionResult> GetTranscript(int id) { var partitionKey = KeyUtils.IntToKey(id); var filterPartition = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey); // Since we do RowKey = KeyUtils.DateTimeToDescKey(now), the last written record is at the top. var query = new TableQuery <GameCopycatEntity2>().Where(filterPartition).Take(1); var entities = await AzureStorageUtils.ExecuteQueryAsync(AzureStorageUtils.TableNames.GameCopycat, query); var result = entities.Any() ? entities.Single() : new object(); return(Ok(result)); }
public async Task RegisterWatcher(int exerciseId) { var userId = AccountUtils.GetUserId(Context.User.Identity); const string sql = @"select UserId from dbo.exeExercises where Id = @ExerciseId;"; var authorUserId = (await DapperHelper.QueryResilientlyAsync <int>(sql, new { ExerciseId = exerciseId, })).Single(); if (userId == authorUserId) { var groupName = KeyUtils.IntToKey(exerciseId); // Corresponds to PieceTypes.PartitionKey await Groups.Add(Context.ConnectionId, groupName); } /* You should not manually remove the connection from the group when the user disconnects. This action is automatically performed by the SignalR framework. */ }
public async Task <IHttpActionResult> PostResourceView([FromUri] int id, [FromBody] JObject value) { // A suggestion may have resourceId == null(0?) when the resource is made on the client in code and points to Google // TODO. Idea: Get the unauthenicated user's session cookie and log views for the session in a separate datastore. var userId = this.GetUserId(); // GetUserId() returns 0 for an unauthenticated user. That's fine. We log every view. var logEnity = new DynamicTableEntity(KeyUtils.GetCurrentTimeKey(), KeyUtils.IntToKey(userId)); logEnity["Json"] = new EntityProperty(value.ToString()); var logTask = AzureStorageUtils.InsertEntityAsync(AzureStorageUtils.TableNames.LibraryLog, logEnity); if (userId != 0 && id != 0) { var viewTask = DapperHelper.ExecuteResilientlyAsync("dbo.libPostResourceView", new { UserId = userId, ResourceId = id, }, CommandType.StoredProcedure); // We use KeyUtils.LocalTimeToInvertedKey() to keep the local time and to order last records first for retrieval. var localTime = (string)value["localTime"]; var rowKey = KeyUtils.LocalTimeToInvertedKey(localTime); if (String.IsNullOrEmpty(rowKey)) { throw new ArgumentOutOfRangeException(localTime); } var historyEntity = new LibraryHistoryEntity { PartitionKey = KeyUtils.IntToKey(userId), RowKey = rowKey, ResourceId = id, }; var historyTask = AzureStorageUtils.InsertEntityAsync(AzureStorageUtils.TableNames.LibraryHistory, historyEntity); // Do all the tasks in parallel. await Task.WhenAll(viewTask, historyTask, logTask); } else { await logTask; } return(StatusCode(HttpStatusCode.NoContent)); }
public async Task <IHttpActionResult> GetFromFreeswitch(string uuid, int userId = 0) { RecordingDetails result = null; // Produce file paths. var workDirPath = GeneralUtils.GetAppDataDir(); var sourceFilePath = Path.Combine(workDirPath, uuid + ".wav"); var outputFilePath = Path.ChangeExtension(sourceFilePath, "mp3"); // The path structure in the Blob Storage is userIdKey/timeKey/filename.ext var userIdKey = KeyUtils.IntToKey(userId); var timeKey = KeyUtils.GetTimeAsBase32(); var outputBlobName = String.Format("{0}/{1}/{2}.mp3", userIdKey, timeKey, uuid); var logBlobName = Path.ChangeExtension(outputBlobName, "log"); try { // Convert to MP3. Increase the audio volume by 10dB, convert to MP3 CBR 64kbit/s. var arguments = String.Format("-i \"{0}\" -af \"volume=10dB\" -b:a 64k \"{1}\"", sourceFilePath, outputFilePath); var logText = RecordingUtils.RunFfmpeg(arguments); var containerName = AzureStorageUtils.ContainerNames.Artifacts; var taskMp3 = AzureStorageUtils.UploadFromFileAsync(outputFilePath, containerName, outputBlobName, "audio/mpeg"); var taskLog = AzureStorageUtils.UploadTextAsync(logText, containerName, logBlobName, "text/plain"); // Upload the blobs simultaneously. await Task.WhenAll(taskMp3, taskLog); // Get the recording's duration. var duration = RecordingUtils.GetDurationFromFfmpegLogOrMp3File(logText, outputFilePath); // Delete the original WAV file on success. File.Delete(sourceFilePath); // The JSON encoder with default settings doesn't make upper-case -> lower-case letter conversion of property names. The receiving side is case-sensitive. result = new RecordingDetails { BlobName = outputBlobName, TotalDuration = duration, }; } finally { // Clean up the MP3 file anyway. File.Delete(outputFilePath); } return(Ok <RecordingDetails>(result)); }
public async Task <IHttpActionResult> PutPresentation([FromBody] JObject value) { var text = (string)value["presentation"]; if (text != null) { using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(text))) { var blobName = KeyUtils.IntToKey(this.GetUserId()); await AzureStorageUtils.UploadBlobAsync(stream, AzureStorageUtils.ContainerNames.Presentations, blobName, "text/plain; charset=utf-8"); // CloudBlockBlob.UploadTextAsync() writes ContentType "application/octet-stream". } } return(StatusCode(text != null ? HttpStatusCode.NoContent : HttpStatusCode.BadRequest)); }
public async Task <IHttpActionResult> GetAllReviewPieces(int exerciseId) { var filterPartition = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, ReviewPiece.GetPartitionKey(exerciseId)); var query = new TableQuery <ReviewPiece>().Where(filterPartition); var allPieces = await AzureStorageUtils.ExecuteQueryAsync(AzureStorageUtils.TableNames.ReviewPieces, query); // Enforce access rights. The exercise author cannot see review items in an unfinished review. An access entity is written when a review is finished. See ReviewsApiController.PostFinishReview var userAccessCode = ReviewPiece.PieceTypes.Viewer + KeyUtils.IntToKey(this.GetUserId()); // Find the ReviewIds which are allowed to access. var reviewIds = allPieces .Where(i => ReviewPiece.GetUserAccessCode(i.RowKey) == userAccessCode) .Select(i => ReviewPiece.GetReviewId(i.RowKey)) .ToList(); RemoveAccessEntries(allPieces); // Filter the record set. var accessablePieces = allPieces.Where(i => reviewIds.Contains(ReviewPiece.GetReviewId(i.RowKey))); var piecesArr = accessablePieces.Select(i => i.Json).ToArray(); return(Ok(piecesArr)); }
public async Task <ActionResult> Claim(string id) { var userIdKey = KeyUtils.IntToKey(0); var longTimeKey = id + this.GetExtId(); var blobName = ExerciseUtils.FormatBlobName(userIdKey, longTimeKey, "metadata", "json"); var blob = AzureStorageUtils.GetBlob(AzureStorageUtils.ContainerNames.Artifacts, blobName); var metadataJson = await blob.DownloadTextAsync(); var metadata = JObject.Parse(metadataJson); var serviceType = (string)metadata["serviceType"]; var cardId = (Guid?)metadata["cardId"]; var title = (string)metadata["title"]; var comment = (string)metadata["comment"]; var details = metadata["recordingDetails"]; var recordingDetails = details.ToObject <RecordingDetails>(); var exerciseId = await ExerciseUtils.CreateExercise(recordingDetails.BlobName, this.GetUserId(), serviceType, ArtifactType.Mp3, recordingDetails.TotalDuration, title, cardId, comment, details.ToString(Formatting.None)); //~~ Redirect to the View exercise page. return(RedirectToAction("View", new { Id = exerciseId })); }
private async Task <DapperHelper.PageItems <ResourceDto> > GetHistoryResources(int userId, string secondaryFilter, int offset = 0, int limit = 0) { var result = new DapperHelper.PageItems <ResourceDto>(); var partitionKey = KeyUtils.IntToKey(userId); var filterPartition = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey); var combinedFilter = !String.IsNullOrEmpty(secondaryFilter) ? TableQuery.CombineFilters(filterPartition, TableOperators.And, secondaryFilter) : filterPartition; var query = new TableQuery <LibraryHistoryEntity>().Where(combinedFilter); var entities = await AzureStorageUtils.ExecuteQueryAsync(AzureStorageUtils.TableNames.LibraryHistory, query); result.TotalCount = entities.Count(); if (entities.Any()) { var historyItems = (limit != 0) ? entities.Skip(offset).Take(limit) : entities; result.Items = await HydrateHistoryItems(historyItems, userId); } return(result); }
// GET: /history public async Task <ActionResult> History() { // Send all the days there are records for. We will enable/disable days in the calendar on the page accordingly. RowKeys in the table are "inverted" local time. var days = new List <string>(); if (this.IsAuthenticated()) { var userId = this.GetUserId(); var partitionKey = KeyUtils.IntToKey(userId); var filterPartition = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey); var query = new TableQuery <TableEntity>().Where(filterPartition); var table = AzureStorageUtils.GetCloudTable(AzureStorageUtils.TableNames.LibraryHistory); TableQuerySegment <TableEntity> currentSegment = null; while (currentSegment == null || currentSegment.ContinuationToken != null) { currentSegment = await table.ExecuteQuerySegmentedAsync <TableEntity>( query, currentSegment != null?currentSegment.ContinuationToken : null ); // Format 2014-01-21 as "140121" var localDays = currentSegment.Results .GroupBy(i => i.RowKey.Substring(0, 6)) .Select(i => KeyUtils.InvertedKeyToLocalTime(i.Key, 3, "", "d2").Substring(2)) ; days.AddRange(localDays); } } //var daysParam = days.Distinct(); //ViewBag.DaysParamJson = JsonUtils.SerializeAsJson(daysParam); ViewBag.DaysParam = days.Distinct(); return(View()); }
// TODO OBSOLETE. Do not rename the files parameter. The form inputs are bounded by this name. public async Task <ActionResult> SaveRecordings(IEnumerable <HttpPostedFileBase> files, string serviceType, Guid?cardId, string title, string comment) { /* * 1. Validate input. * 2. Save the original media files to blobs. * 3. Call the remote transcoding service. The service converts files to MP3, merges them into a single file, saves it to a blob and returns its name and the recording's duration. * 4. Create a database record. * 5. Redirect to the exercise page. */ // 1. Validate input. // Find out the action to redirect to on error. We use the referrer string. RedirectToAction seems accept a case-insensitive parameter. var referrerAction = (Request.UrlReferrer.Segments.Skip(2).Take(1).SingleOrDefault() ?? "Index").Trim('/'); //referrerAction = referrerAction.First().ToString().ToUpper() + referrerAction.Substring(1).ToLower(); // var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext)); if (files == null || files.All(i => i == null)) { return(RedirectToAction(referrerAction, new { error = "no_file" })); } // There may be empty form parts from input elements with no file selected. var originalFiles = files.Where(i => i != null); var contentType = originalFiles .Select(i => i.ContentType) .Distinct() .Single() ; var acceptedContentTypes = (new[] { MediaType.Mpeg, MediaType.Mp3, MediaType.Amr, MediaType.Gpp, MediaType.QuickTime }); if (!acceptedContentTypes.Contains(contentType)) { return(RedirectToAction(referrerAction, new { error = "wrong_file_format" })); } // 2. Save the original media files to blobs. var userId = this.GetUserId(); var userIdKey = KeyUtils.IntToKey(userId); var timeKey = KeyUtils.GetTimeAsBase32(); var index = 0; var extension = MediaType.GetExtension(contentType); //var blobNames = new List<string>(); foreach (var file in originalFiles) { using (var inputStream = file.InputStream) { // The directory structure in the Blob Storage is userIdKey/timeKey/index.ext. Runnymede.Helper.Controllers.RecordingsController.Get() relies on this structure. var blobName = String.Format("{0}/{1}/{2}.{3}", userIdKey, timeKey, index, extension); await AzureStorageUtils.UploadBlobAsync(inputStream, AzureStorageUtils.ContainerNames.Artifacts, blobName, file.ContentType); index++; } } // 3. Call the remote transcoding service. var host = ConfigurationManager.AppSettings["RecorderHost"]; var urlFormat = "http://{0}/api/recordings/transcoded/?userIdKey={1}&timeKey={2}&extension={3}&count={4}"; var url = String.Format(urlFormat, host, userIdKey, timeKey, extension, originalFiles.Count()); HttpClient client = new HttpClient(); HttpResponseMessage response = await client.GetAsync(url); if (!response.IsSuccessStatusCode) { return(RedirectToAction(referrerAction, new { error = "transcoding_error" })); } // Error is returned as HTML. Then we get error here: No MediaTypeFormatter is available to read an object of type 'JObject' from content with media type 'text/html'. var recordingDetails = await response.Content.ReadAsAsync <RecordingDetails>(); // Make sure the duration is known. If the transcoder has failed to parse the ffmpeg logs, it returns DurationMsec = 0. if (recordingDetails.TotalDuration == 0) { // Read the blob and try to determine the duration directly. recordingDetails.TotalDuration = await RecordingUtils.GetMp3Duration(AzureStorageUtils.ContainerNames.Artifacts, recordingDetails.BlobName); } // 4. Create a database record. var exerciseId = await ExerciseUtils.CreateExercise(recordingDetails.BlobName, userId, serviceType, ArtifactType.Mp3, recordingDetails.TotalDuration, title, cardId, comment); // 5. Redirect to the exercise page. return(RedirectToAction("View", "Exercises", new { Id = exerciseId })); }
public async Task <IHttpActionResult> PostTracks(string metadataName) { //~~ Validate. if (!Request.Content.IsMimeMultipartContent()) { return(BadRequest()); } // Parse the request body parts. var streamProvider = await Request.Content.ReadAsMultipartAsync(); // Check mediaType of the files var mediaTypes = streamProvider.Contents .Select(i => i.Headers) .Where(i => i.ContentDisposition.Name.Trim('"') != metadataName) .Select(i => i.ContentType) //.Where(i => i != null) // ContentType is null in the 'metadata' part. .Select(i => i.MediaType) .Distinct() ; var mediaType = mediaTypes.FirstOrDefault(); var acceptedMediaTypes = new[] { MediaType.Mpeg, MediaType.Mp3, MediaType.Amr, MediaType.Gpp, MediaType.QuickTime }; if ((mediaTypes.Count() != 1) || !acceptedMediaTypes.Contains(mediaType)) { return(BadRequest(String.Join(", ", mediaTypes))); } //~~ Save the original media files to blobs. var userId = this.GetUserId(); // May be 0 if the user is unauthenticated var userIdKey = KeyUtils.IntToKey(userId); var timeKey = KeyUtils.GetTimeAsBase32(); // The length of extId is 12 chars. var extId = (userId != 0) ? null : this.GetExtId(); var longTimeKey = timeKey + extId; // If an operand of string concatenation is null, an empty string is substituted. var extension = MediaType.GetExtension(mediaType); var tracks = new List <KeyValuePair <string, MemoryStream> >(); var fileNames = new List <string>(); foreach (var content in streamProvider.Contents) { var name = content.Headers.ContentDisposition.Name.Trim('"'); var contentType = content.Headers.ContentType; // We have checked above, if ContentType has a value, it has the right MediaType. if ((name != metadataName) && (contentType != null)) { fileNames.Add(name); var stream = await content.ReadAsStreamAsync(); using (stream) { var memStream = new MemoryStream(); await stream.CopyToAsync(memStream); var pair = new KeyValuePair <string, MemoryStream>(name, memStream); tracks.Add(pair); } } } // Upload all blobs in parallel. var tasks = tracks.Select(i => { var blobName = ExerciseUtils.FormatBlobName(userIdKey, longTimeKey, i.Key, extension); return(AzureStorageUtils.UploadBlobAsync(i.Value, AzureStorageUtils.ContainerNames.Artifacts, blobName, mediaType)); }); await Task.WhenAll(tasks); //~~ Call the transcoding service. ///* We send the file names as a comma separated list. There is also a binding in the Web API like this: //public IHttpActionResult GetFoo([FromUri] int[] ids); Call: /Foo?ids=1&ids=2&ids=3 or /Foo?ids[0]=1&ids[1]=2&ids[2]=3 //public IHttpActionResult GetFoo([FromUri] List<string> ids); Call: /Foo?ids[]="a"&ids[]="b"&ids[]="c" //*/ //var host = ConfigurationManager.AppSettings["RecorderHost"]; //var urlFormat = "http://{0}/api/recordings/transcoded/?userIdKey={1}&timeKey={2}&extension={3}&fileNames={4}"; //var url = String.Format(urlFormat, host, userIdKey, longTimeKey, extension, String.Join(",", fileNames)); //HttpClient client = new HttpClient(); //HttpResponseMessage response = await client.GetAsync(url); //if (!response.IsSuccessStatusCode) //{ // // return RedirectToAction(referrerAction, new { error = "transcoding_error" }); // return InternalServerError(new Exception("Transcoding error. " + response.StatusCode.ToString())); //} //// Error is returned as HTML. Then we get error here: No MediaTypeFormatter is available to read an object of type 'JObject' from content with media type 'text/html'. //var recordingDetails = await response.Content.ReadAsAsync<RecordingDetails>(); //// Make sure the duration is known. If the transcoder has failed to parse the ffmpeg logs, it returns DurationMsec = 0. //if (recordingDetails.TotalDuration == 0) //{ // // Read the blob and try to determine the duration directly. // recordingDetails.TotalDuration = // await RecordingUtils.GetMp3Duration(AzureStorageUtils.ContainerNames.Artifacts, recordingDetails.BlobName); //} var transcoder = new RecordingTranscoder(); var recordingDetails = await transcoder.Transcode(tracks, userIdKey, longTimeKey, extension); // Release the memory streams. tracks.ForEach(i => { i.Value.Dispose(); }); //~~ Read the metadata. // Chrome wraps the part name in double-quotes. var metadataContent = streamProvider.Contents .Single(i => i.Headers.ContentDisposition.Name.Trim('"') == metadataName) ; var metadataJson = await metadataContent.ReadAsStringAsync(); var metadata = JObject.Parse(metadataJson); var serviceType = (string)metadata["serviceType"]; var cardId = (Guid?)metadata["cardId"]; var title = (string)metadata["title"]; var comment = (string)metadata["comment"]; var serializer = new JsonSerializer() { ContractResolver = new CamelCasePropertyNamesContractResolver() }; var details = JObject.FromObject(recordingDetails, serializer); if (userId != 0) { //~~ Create a database record. var exerciseId = await ExerciseUtils.CreateExercise(recordingDetails.BlobName, userId, serviceType, ArtifactType.Mp3, recordingDetails.TotalDuration, title, cardId, comment, details.ToString(Formatting.None)); //~~ The client will redirect to the View exercise page. return(Ok(new { ExerciseId = exerciseId })); } else { //~~ Save the details for future use. metadata.Add(new JProperty("recordingDetails", details)); var blobName = ExerciseUtils.FormatBlobName(userIdKey, longTimeKey, "metadata", "json"); await AzureStorageUtils.UploadTextAsync(metadata.ToString(), AzureStorageUtils.ContainerNames.Artifacts, blobName, MediaType.Json); //~~ The client will redirect to the Signup page. longTimeKey will be built from timeKey on the Claim page. return(Ok(new { Key = timeKey })); } }
public async Task <ActionResult> SaveRecordingMobile(HttpPostedFileBase fileInput, string recorderId, string recordingTitle) { /* * 1. Save the original media file to a blob * 2. Call the remote Transcoding service. Pass the blob name. * 3. The Transcoding service saves the MP3 file to a blob and returns its name and the recording's duration. * 4. Create a database record. */ if (fileInput == null) { return(RedirectToAction("RecordSpeech", new { error = "no_file" })); } // return RedirectToAction("RecordSpeech", new { error = "test01" }); var contentType = fileInput.ContentType; var acceptedContentType = (new[] { MediaType.Amr, MediaType.Gpp, MediaType.QuickTime }).Contains(contentType); if (!acceptedContentType) { return(RedirectToAction("RecordSpeech", new { error = contentType })); } var userId = this.GetUserId(); // 1. Save the original file. // The directory structure in Blob Storage is userIdKey/timeKey/originalFileName.ext var timeKey = KeyUtils.GetTimeAsBase32(); var fileName = fileInput.FileName; // Sanitize the fileName. Reserved URL characters must be properly escaped. fileName = String.IsNullOrWhiteSpace(fileName) ? timeKey : Uri.EscapeUriString(fileName.Trim()); var invalidChars = Path.GetInvalidFileNameChars(); fileName = new String(fileName.Select(i => invalidChars.Contains(i) ? '_' : i).ToArray()); if (!Path.HasExtension(fileName)) { Path.ChangeExtension(fileName, MediaType.GetExtension(contentType)); } var blobName = KeyUtils.IntToKey(userId) + AzureStorageUtils.DefaultDirectoryDelimiter + timeKey + AzureStorageUtils.DefaultDirectoryDelimiter + fileName; using (var stream = fileInput.InputStream) { await AzureStorageUtils.UploadBlobAsync(stream, AzureStorageUtils.ContainerNames.Recordings, blobName, contentType); } // 2. Call the transcoding service. var host = ConfigurationManager.AppSettings["RecorderHost"]; var url = String.Format("http://{0}/api/recordings/transcoded/?inputBlobName={1}", host, blobName); HttpClient client = new HttpClient(); HttpResponseMessage response = await client.GetAsync(url); if (!response.IsSuccessStatusCode) { return(RedirectToAction("RecordSpeech", new { error = "transcoding_error" })); } // 3. Get the results from the service. // Error is returned as HTML. Then we get error here: No MediaTypeFormatter is available to read an object of type 'JObject' from content with media type 'text/html'. var value = await response.Content.ReadAsAsync <JObject>(); var outputBlobName = (string)value["outputBlobName"]; var duration = Convert.ToDecimal((int)value["durationMsec"] / 1000.0m); // The transcoder may return -1 if it has failed to parse the ffmpeg logs. if (duration < 0) { // Read the blob and try to determine the duration directly. duration = await RecordingUtils.GetMp3Duration(AzureStorageUtils.ContainerNames.Recordings, outputBlobName); } // 4. Create a database record. var exerciseId = await ExerciseUtils.CreateExercise(outputBlobName, userId, ServiceType.IeltsSpeaking, ArtifactType.Mp3, duration, recordingTitle); // 5. Redirect to the exercise page. return(RedirectToAction("View", new { Id = exerciseId })); }
public static string GetRowKey(int reviewId, string pieceType, int id) { // Corresponds to app.reviews.Editor.getRowKey() return KeyUtils.IntToKey(reviewId) + pieceType + KeyUtils.IntToKey(id); }
public static string GetPartitionKey(int exerciseId) { // Corresponds to app.reviews.Editor.getPartitionKey() return KeyUtils.IntToKey(exerciseId); }
public async Task <ActionResult> SavePhotos(IEnumerable <HttpPostedFileBase> files, IEnumerable <int> rotations, string serviceType, Guid?cardId, string title, string comment) { // Find out the action to redirect to on error. We use the referrer string. RedirectToAction seems accept a case-insensitive parameter. var referrerAction = (Request.UrlReferrer.Segments.Skip(2).Take(1).SingleOrDefault() ?? "Index").Trim('/'); //referrerAction = referrerAction.First().ToString().ToUpper() + referrerAction.Substring(1).ToLower(); // var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext)); if (files == null || files.All(i => i == null)) { return(RedirectToAction(referrerAction, new { error = "no_file" })); } if (files.Count() != rotations.Count()) { return(RedirectToAction(referrerAction, new { error = "wrong_rotations" })); } var items = files .Select((i, idx) => new { File = i, Rotation = rotations.ElementAt(idx) }) // There may be empty form parts from input elements with no file selected. .Where(i => i.File != null); if (!items.All(i => i.File.ContentType == MediaType.Jpeg)) { return(RedirectToAction(referrerAction, new { error = "wrong_file_format" })); } var userId = this.GetUserId(); var userKey = KeyUtils.IntToKey(userId); var timeKey = KeyUtils.GetTimeAsBase32(); var page = 1; // Pages start counting from 1. var blobNames = new List <string>(); foreach (var item in items) { using (var inputStream = item.File.InputStream) { var blobName = String.Format("{0}/{1}/{2}.original.jpg", userKey, timeKey, page); await AzureStorageUtils.UploadBlobAsync(inputStream, AzureStorageUtils.ContainerNames.Artifacts, blobName, MediaType.Jpeg); using (var memoryStream = new MemoryStream()) { /* I am not sure about using JpegBitmapDecoder. Because of the native code dependencies, the PresentationCore and WindowsBase assemblies need to be distributed as x86 and x64, so AnyCPU may be not possible? */ inputStream.Seek(0, SeekOrigin.Begin); using (var image = Image.FromStream(inputStream)) { RotateFlipType rotateFlipType = RotateFlipType.RotateNoneFlipNone; switch (item.Rotation) { case -1: rotateFlipType = RotateFlipType.Rotate270FlipNone; break; case 1: rotateFlipType = RotateFlipType.Rotate90FlipNone; break; default: break; } ; if (rotateFlipType != RotateFlipType.RotateNoneFlipNone) { image.RotateFlip(rotateFlipType); } // We re-encode the image to decrease the size. var codec = ImageCodecInfo.GetImageEncoders().FirstOrDefault(c => c.FormatID == ImageFormat.Jpeg.Guid); var encoderParams = new EncoderParameters(1); // Highest quality is 100. Quality affects the file size. Do not change it until you have exprimented. int quality = 50; // Do not pass inline. This parameter is passed via pointer and it has to be strongly typed. encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, quality); image.Save(memoryStream, codec, encoderParams); //image.Save(memoryStream, ImageFormat.Jpeg); } memoryStream.Seek(0, SeekOrigin.Begin); blobName = String.Format("{0}/{1}/{2}.jpg", userKey, timeKey, page); await AzureStorageUtils.UploadBlobAsync(memoryStream, AzureStorageUtils.ContainerNames.Artifacts, blobName, MediaType.Jpeg); blobNames.Add(blobName); } page++; } } var artifact = String.Join(",", blobNames); var exerciseId = await ExerciseUtils.CreateExercise(artifact, userId, serviceType, ArtifactType.Jpeg, 0, title, cardId, comment); return(RedirectToAction("View", "Exercises", new { Id = exerciseId })); }
public async Task <IHttpActionResult> GetPresentation(int id) { // TODO. Enable CORS on the Azure blob and download presentation directly using AJAX. +http://odetocode.com/blogs/scott/archive/2014/03/31/http-clients-and-azure-blob-storage.aspx // +http://blog.cynapta.com/2013/12/cynapta-azure-cors-helper-free-tool-to-manage-cors-rules-for-windows-azure-blob-storage/ var blob = AzureStorageUtils.GetBlob(AzureStorageUtils.ContainerNames.Presentations, KeyUtils.IntToKey(id)); var text = await blob.DownloadTextAsync(); //var text = await AzureStorageUtils.GetBlobAsText(AzureStorageUtils.ContainerNames.Presentations, KeyUtils.IntToKey(id)); return(new RawStringResult(this, text, RawStringResult.TextMediaType.PlainText)); }
public async Task <IHttpActionResult> GetReviewPieces(int exerciseId, int reviewId) { var filterPartition = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, ReviewPiece.GetPartitionKey(exerciseId)); var filterRowFrom = TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThanOrEqual, KeyUtils.IntToKey(reviewId)); var filterRowTo = TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.LessThanOrEqual, KeyUtils.IntToKey(reviewId) + "Z"); string combinedRowFilter = TableQuery.CombineFilters(filterRowFrom, TableOperators.And, filterRowTo); string combinedFilter = TableQuery.CombineFilters(filterPartition, TableOperators.And, combinedRowFilter); var query = new TableQuery <ReviewPiece>().Where(combinedFilter); var pieces = await AzureStorageUtils.ExecuteQueryAsync(AzureStorageUtils.TableNames.ReviewPieces, query); // The access entity was written at the review start. See ReviewsApiController.PostStartReview var userRowKey = ReviewPiece.GetRowKey(reviewId, ReviewPiece.PieceTypes.Viewer, this.GetUserId()); if (pieces.Any(i => i.RowKey == userRowKey)) { RemoveAccessEntries(pieces); } else { pieces.Clear(); } var piecesArr = pieces.Select(i => i.Json).ToArray(); return(Ok(piecesArr)); }