I'm uploading a file (video) to a server and need to play it after processing it (there's a webservice to query the processing status).
How can I add a video player (maybe the HTML5 video tag) to a DIV only after the response of this webservice?
Current code to load the video causes unnecessary server request to a non existing url.
Angular filter to allow the url
app.filter("trustUrl", ['$sce', function ($sce) {
return function (recordingUrl) {
return $sce.trustAsResourceUrl(recordingUrl);
};
}]);
HTML
<video src="{{encodedFileUrl | trustUrl}}" controls></video>
You may use Videogular, angular based video player
Related
I need to upload image and video files to the server in an Angular application using Laravel 5.1 as the back end. All Ajax requests need to go to the Laravel controller first, and we have the code there for how the file gets handled when it gets there. We have previously done normal HTML forms to submit file uploads to the controller, but in this case we need to avoid the page refresh of a form, so I am attempting this in Ajax through Angular.
What information do I need to send to the Laravel controller with Ajax that was being sent to the controller via an HTML form previously?
This is the code in the Laravel controller that handled the file information once it got there. That's what I need to figure out how to send, so I can hopefully reuse this code:
$promotion = Promotion::find($id);
if (Input::hasFile('img_path')){
$path = public_path().'/images/promotion/'.$id.'/';
$file_path = $path.'promotion.png';
$delete = File::delete($file_path);
$file = Input::file('img_path');
$uploadSuccess = $file->move($path, 'promotion.png');
$promotion->img_path = '/images/promotion/'.$id.'/promotion.png';
}
if (Input::hasFile('video_path')){
$path = public_path().'/video/promotion/'.$id.'/';
$file_path = $path.'promotion.mp4';
$delete = File::delete($file_path);
$file = Input::file('video_path');
$uploadSuccess = $file->move($path, 'promotion.mp4');
$promotion->video_path = '/video/promotion/'.$id.'/promotion.mp4';
}
As you can see above, we are converting whatever file we get to a PNG with the file name promotion.png so it's easy to fetch, and we are only accepting .mp4 video format. Because of that, we don't need to worry about checking if the file exists and is it ok to overwrite it. That's why you can see in the code we delete any existing file of that name before saving.
The HTML was just an input with a type of "file:
<input type="file" id="img_path" name="img_path" class="promo-img-path" accept="image/*">
We are using Angular now so I can't just send the above through an HTML form anymore. That's what I need to figure out how to do.
We are two developers just doing our best, so I'm sure there is a better way of doing this. However before I refactor this whole thing, I'm hoping I can use Angular (or jQuery as a last resort) to just send the controller whatever file data Laravel needs in order to make the above code work. The answer may be as simple as "send a PUT to the method in that controller above, but instead of a normal JSON payload, use file info in this format and you can gather that info with..."
I would also appreciate any tips on better ways I can do this in the future.
How to POST FormData Using the $http Service
When using the FormData API to POST files and data, it is important to set the Content-Type header to undefined.
var fd = new FormData()
for (var i in $scope.files) {
fd.append("fileToUpload", $scope.files[i]);
}
var config = {headers: {'Content-Type': undefined}};
var httpPromise = $http.post(url, fd, config);
By default the AngularJS framework uses content type application/json. By setting Content-Type: undefined, the AngularJS framework omits the content type header allowing the XHR API to set the content type. When sending a FormData object, the XHR API sets the content type to multipart/form-data with the proper boundaries and base64 encoding.
For more information, see MDN Web API Reference - XHR Send method
How did you get the file information into $scope.files?
How to enable <input type="file"> to work with ng-model
This directive also enables <input type="file"> to automatically work with the ng-change and ng-form directives.
angular.module("app",[]);
angular.module("app").directive("selectFilesNg", function() {
return {
require: "ngModel",
link: function postLink(scope,elem,attrs,ngModel) {
elem.on("change", function(e) {
var files = elem[0].files;
ngModel.$setViewValue(files);
})
}
}
});
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<h1>AngularJS Input `type=file` Demo</h1>
<input type="file" select-files-ng ng-model="fileArray" multiple>
<code><table ng-show="fileArray.length">
<tr><td>Name</td><td>Date</td><td>Size</td><td>Type</td><tr>
<tr ng-repeat="file in fileArray">
<td>{{file.name}}</td>
<td>{{file.lastModified | date : 'MMMdd,yyyy'}}</td>
<td>{{file.size}}</td>
<td>{{file.type}}</td>
</tr>
</table></code>
</body>
RECOMMENDED: POST Binary Files Directly
Posting binary files with multi-part/form-data is inefficient as the base64 encoding adds an extra 33% overhead. If the server API accepts POSTs with binary data, post the file directly.
See How to POST binary files with AngularJS (with DEMO)
I am using nervgh/angular-file-upload to upload files. As I know, this angular-file-upload has their own upload functions.
Currently I need only the files inside the uploader, and then I will use my custom http post
$scope.UploadUserFiles = function () {
$scope.UserFileRequest = {"File": uploader, "User": myUser}
$http.post(host + "/api/user", $scope.UserFileRequest).then(function (response) { });
}
Currently I am having the trouble of getting those files from the uploader since I have no idea about the properties and structure. But for User is my own class at the web api, so I have no problem to get any info that I want.
The main problem is I have no idea how to catch/initialize the uploader it into a variable after post to my web api.
[HttpPost]
[Route("api/user")]
public UserFileResponse GetUserFile(UserFileRequest userFileRequest)
{
//What should I do to get the Files from the request and save into the server?
}
My UserFileRequest contain the info of myUser, but I do not know what to do with the uploader since I do not have a class to hold it.
From what I understand in your question, you are having trouble getting the files in the queue into your own file upload function.
The functions within the FileUploader class have callbacks that you can use, but another way to easily expose the file queue would be to create your own callback once the 'upload' button is clicked.
someFxn(item) {
console.log(item);
_someUploadFxn(item)
}
<md-button class="md-blue" ng-click="$ctrl.someFxn(item)" ng-disabled="item.isReady || item.isUploading || item.isSuccess">
Upload
</md-button>
This will allow you to view the file that has been queued up and from there you can pass it or whatever properties you need off that file object into your own uploader function.
The same goes for if you are wanting to upload a bunch of files at once.
I'm working on an angularjs SPA type project and there I came across a situation where i need to play an ordinary mp4 file, but the issue is that video is not public so in order to access it i need to send an access token in header.
so my question is
If this is possible how do i do the playback
is token based authentication is a right approach to securely access a media
At the end I came up with this code, code you see is a part of a directive
var video = $http({
method : "GET",
url : "http://path/small1.mp4",
responseType : "blob",
headers : { X-Access-Token : "token" }
});
video.success(function(data,status,headers,config){
if((data != undefined)){
var dataURL= window.URL.createObjectURL(data);
$scope.videourl = dataURL;
//window.location =dataURL;
var video = document.getElementById("emptyvideo");
video.src = dataURL;
video.addEventListener('error', function(err){
// Nothing to see here...
console.log(err);
// Will throw a MediaError code 4
console.log(video.error);
});
}else{
alert("error");
}
});
video.error(function(data,status){
alert("error");
});
This is fine for small videos, but if the video got bigger this is not a good apporach. because in this code video need to be fully loaded in order to do a playback, please suggest me if there is a better apporach
In order not to loose streaming ability, the best (if not the only) way is to secure static videos with an access token passed as an url parameter. I mean you need to give up authentication via token in header.
So you need to make changes to your service on your server to authenticate request by query parameter.
For example your url will look like: www.yourawesomeserver/your_video?access_token=ue873wijweu383j3
Your server should serve the static file if the token is OK.
I am using Angular js to show loading screen. It works for all the REST services call except REST service to download the file. I understand why it is not working because for download I am not making any service call using $resource; instead of that I am using normal approach to download the file therefore Angular js code doesn't have any control on start/finish the service request. I tried to use $resource to hit this REST service however I am getting the data from this service and in this case loading screen was working fine however not sure how to use this data to display to user to download in angular way. Following are required details. Please help.
Approach 1 using iframe approach:
/*Download file */
scope.downloadFile = function (fileId) {
//Show loading screen. (Somehow it is not working)
scope.loadingProjectFiles=true;
var fileDownloadURL = "/api/files/" + fileId + "/download";
downloadURL(fileDownloadURL);
//Hide loading screen
scope.loadingProjectFiles=false;
};
var $idown; // Keep it outside of the function, so it's initialized once.
var downloadURL = function (url) {
if ($idown) {
$idown.attr('src', url);
} else {
$idown = $('<iframe>', { id: 'idown', src: url }).hide().appendTo('body');
}
};
Approach 2 using $resource (Not sure how to display data on screen to download)
/*Download file */
scope.downloadFile = function (fileId) {
//Show loading screen (Here loading screen works).
scope.loadingProjectFiles=true;
//File download object
var fileDownloadObj = new DownloadFile();
//Make server call to create new File
fileDownloadObj.$get({ fileid: fileid }, function (response) {
//Q? How to use the response data to display on UI as download popup
//Hide loading screen
scope.loadingProjectFiles=false;
});
};
This is the correct pattern with the $resource service:
scope.downloadFile = function (fileId) {
//Show loading screen (Here loading screen works).
scope.loadingProjectFiles=true;
var FileResource = $resource('/api/files/:idParam', {idParam:'#id'});
//Make server call to retrieve a file
var yourFile = FileResource.$get({ id: fileId }, function () {
//Now (inside this callback) the response data is loaded inside the yourFile variable
//I know it's an ugly pattern but that's what $resource is about...
DoSomethingWithYourFile(yourFile);
//Hide loading screen
scope.loadingProjectFiles=false;
});
};
I agree with you that this is a weird pattern and is different of other APIs where the downloaded data is assigned to a parameter in a callback function, hence your confusion.
Pay attention to the names and the cases of the parameters, and look that there're two mappings involved here, one between the caller to the $resource object and the object itself, and another between this object and the url that it contructs for downloading the actual data.
Here are some idea's for the second approach, you could present the user with a link after the download has happened:
With a "data url". Probably not a good idea for large files.
With a URL like "filesystem:mydownload.zip" You'd first have to save the file with the filesystem API. You can find some inspiration on html5rocks
When I instantiate the following code in an AngularJS app, I get weird data in the transformResponse function (bottom of code). I'm not calling the $resource function from any controller, just loading the script in a browser. The data variable (see code) contains the HTML of the current partial, when loading the app.
This seems odd. Is this the way it's supposed to be?
var buddyServices = angular
.module('buddyServices', ['ng','ngResource'])
.factory('Buddy',
function ($resource) { console.log('resource');
return $resource('http://webservice.buddyplatform.com/v1/:service',
{service:'', BuddyApplicationName: 'xxx',
BuddyApplicationPassword: 'yyy'}
);
}
)
.config(function($httpProvider){
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.transformResponse = function(data) {
console.log(data);
return 'TEST: '+data;
};
});
=== EDIT ===
It just daunted on me: $httpProvider handles all http requests, so a page load is one of those. I'm guessing a bit now, but it seems probable. So, the question then becomes: Is there an "easy" way to constrain the data in the code above to only those requests performed by my service?
transformResponse takes another parameter headersGetter. You can use this to get the headers send with the response. Look for Content-Type header header. It should contain application/json