分类: 服务器

服务器

在前端页面上传文件到服务器方法示例

在前端页面上传文件到服务器方法示例
1.选择图片
<div class=”item1 update_pic” >
<span>摘要图片:</span>
<img src=”${pageContext.request.contextPath}/upload/<s:property value=”article_pic” />” id=”imageview” class=”item1_img” >
<label for=”fileupload” id=”label_file”>上传文件</label>
<input type=”file” name=”upload” id=”fileupload”/>
</div>
/*原理是把本地图片路径:”D(盘符):/image/…”转为”http://…”格式路径来进行显示图片*/
$(“#fileupload”).change(function() {
var $file = $(this);
var objUrl = $file[0].files[0];
//获得一个http格式的url路径:mozilla(firefox)||webkit or chrome
var windowURL = window.URL || window.webkitURL;
//createObjectURL创建一个指向该参数对象(图片)的URL
var dataURL;
dataURL = windowURL.createObjectURL(objUrl);
$(“#imageview”).attr(“src”,dataURL);
console.log($(‘#imageview’).attr(‘style’));
if($(‘#imageview’).attr(‘style’) === ‘display: none;’){
$(‘#imageview’).attr(‘style’,’inline’);
$(‘#imageview’).width(“300px”);
$(‘#imageview’).height(“200px”);
$(‘.update_pic’).attr(‘style’, ‘margin-bottom: 80px;’);
}
});
2.上传图片
1).form表单enctype设置为multipart/form-data.

<!–在from中添加的属性enctype=”multipart/form-data”–>
<form id=”blog_form” action=”${ctx}/article_update.action” method=post enctype=”multipart/form-data”>
<div class=”edit_content”>
<div class=”item1″>
<div>
<span>文章标题:</span>
<input type=”text” class=”am-form-field” name=”article_title” style=”width: 300px”
value=”<s:property value=”article_title” />”
>&nbsp;&nbsp;
</div>
</div>

<input type=”text” name=”article_desc” id=”article_desc” style=”display: none;”>

<div class=”item1″>
<span>所属分类:</span>
<select id=”category_select” name=”category.parentid” style=”width: 150px”>&nbsp;&nbsp;

</select>

<select id=”skill_select” name=”category.cid” style=”width: 150px”>&nbsp;&nbsp;

</select>

</div>

<div class=”item1 update_pic” >
<span>摘要图片:</span>
<img src=”${pageContext.request.contextPath}/upload/<s:property value=”article_pic” />” id=”imageview” class=”item1_img” >
<label for=”fileupload” id=”label_file”>上传文件</label>
<input type=”file” name=”upload” id=”fileupload”/>
</div>

<div id=”editor” name=”article_content” style=”width:900px;height:400px;”></div>
<input type=”hidden” id=”resContent” value=”<s:property value=”article_content”/>”>
<input type=”hidden” name=”article_id” value=”<s:property value=”article_id”/>”>
<input type=”hidden” name=”article_pic” value=”<s:property value=”article_pic”/>”>
<button class=”am-btn am-btn-default” type=”button” id=”send” style=”margin-top: 10px;”>
修改</button>
</div>

</form>
2).服务器提供属性接收

3).上传文件处理

/**
* 文件上传提供的三个属性:
*/
@Setter
private String uploadFileName; // 文件名称
@Setter
private File upload; // 上传文件(文件路径)
@Setter
private String uploadContentType; // 文件类型
public String add() throws IOException {
System.out.println(“add-web层”);
//上传图片
if(upload != null){
//上传文件
//随机生成文件名称
//1.获取文件的扩展名
int index = uploadFileName.lastIndexOf(“.”);
String etx = uploadFileName.substring(index);
//2.随机生成文件名 拼接扩展名
String uuid = UUID.randomUUID().toString();/*生成的文件名是带“-”的,所以要去掉*/
String uuidFileName = uuid.replace(“-“, “”) + etx;
//确定上传的路径
String path = ServletActionContext.getServletContext().getRealPath(“/upload”);
File file = new File(path);
if(!file.exists()){
file.mkdirs();
}
//拼接新的文件路径
File desFile = new File(path + “/” + uuidFileName);
//文件上传
FileUtils.copyFile(upload,desFile);

//设置图片
article.setArticle_pic(uuidFileName);
}
//设置当前时间
article.setArticle_time(new Date().getTime());
System.out.println(article);
//调用业务层保存到数据库当中
articleService.save(article);
return “listres”;
}
3.集成富文本编辑器
umedit下载地址
https://ueditor.baidu.com/website/download.html
添加到页面添加
1).引入js
<script type=”text/javascript” charset=”utf-8″ src=”${ctx }/js/umedit/ueditor.config.js”></script>
<script type=”text/javascript” charset=”utf-8″ src=”${ctx }/js/umedit/ueditor.all.min.js”> </script>
<script type=”text/javascript” charset=”utf-8″ src=”${ctx }/js/umedit/lang/zh-cn/zh-cn.js”></script>
2).在页面当中提供div标签供显示内容
<div id=”editor” name=”article_content” style=”width:900px;height:400px;”></div>
3).在js当中初始化富文本编辑器
var ue = UE.getEditor(‘editor’);

4.富文本编辑器文件上传
1).添加uedit相关jar包

2).配置过滤器

package com.helong.web.filter;

import org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyFilter extends StrutsPrepareAndExecuteFilter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
//获取当前请求
HttpServletRequest request = (HttpServletRequest) req;
//获取请求的地址来进行判断
String requestURI = request.getRequestURI();
if(requestURI.contains(“js/umedit/jsp/controller.jsp”)){
//放行
chain.doFilter(req,res);
}else{
super.doFilter(req,res,chain);
}
}

}
3).json文件配置获取图片路径

/* 前后端通信相关的配置,注释只允许使用多行方式 */
{
/* 上传图片配置项 */
“imageActionName”: “uploadimage”, /* 执行上传图片的action名称 */
“imageFieldName”: “upfile”, /* 提交的图片表单名称 */
“imageMaxSize”: 2048000, /* 上传大小限制,单位B */
“imageAllowFiles”: [“.png”, “.jpg”, “.jpeg”, “.gif”, “.bmp”], /* 上传图片格式显示 */
“imageCompressEnable”: true, /* 是否压缩图片,默认是true */
“imageCompressBorder”: 1600, /* 图片压缩*长边限制 */
“imageInsertAlign”: “none”, /* 插入的图片浮动方式 */
“imageUrlPrefix”: “http://localhost:8080/”, /* 图片访问路径前缀 */
“imagePathFormat”: “/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}”, /* 上传保存路径,可以自定义保存路径和文件名格式 */
/* {filename} 会替换成原文件名,配置这项需要注意中文乱码问题 */
/* {rand:6} 会替换成随机数,后面的数字是随机数的位数 */
/* {time} 会替换成时间戳 */
/* {yyyy} 会替换成四位年份 */
/* {yy} 会替换成两位年份 */
/* {mm} 会替换成两位月份 */
/* {dd} 会替换成两位日期 */
/* {hh} 会替换成两位小时 */
/* {ii} 会替换成两位分钟 */
/* {ss} 会替换成两位秒 */
/* 非法字符 \ : * ? ” < > | */
/* 具请体看线上文档: fex.baidu.com/ueditor/#use-format_upload_filename */

/* 涂鸦图片上传配置项 */
“scrawlActionName”: “uploadscrawl”, /* 执行上传涂鸦的action名称 */
“scrawlFieldName”: “upfile”, /* 提交的图片表单名称 */
“scrawlPathFormat”: “/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}”, /* 上传保存路径,可以自定义保存路径和文件名格式 */
“scrawlMaxSize”: 2048000, /* 上传大小限制,单位B */
“scrawlUrlPrefix”: “”, /* 图片访问路径前缀 */
“scrawlInsertAlign”: “none”,

/* 截图工具上传 */
“snapscreenActionName”: “uploadimage”, /* 执行上传截图的action名称 */
“snapscreenPathFormat”: “/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}”, /* 上传保存路径,可以自定义保存路径和文件名格式 */
“snapscreenUrlPrefix”: “”, /* 图片访问路径前缀 */
“snapscreenInsertAlign”: “none”, /* 插入的图片浮动方式 */

/* 抓取远程图片配置 */
“catcherLocalDomain”: [“127.0.0.1”, “localhost”, “img.baidu.com”],
“catcherActionName”: “catchimage”, /* 执行抓取远程图片的action名称 */
“catcherFieldName”: “source”, /* 提交的图片列表表单名称 */
“catcherPathFormat”: “/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}”, /* 上传保存路径,可以自定义保存路径和文件名格式 */
“catcherUrlPrefix”: “”, /* 图片访问路径前缀 */
“catcherMaxSize”: 2048000, /* 上传大小限制,单位B */
“catcherAllowFiles”: [“.png”, “.jpg”, “.jpeg”, “.gif”, “.bmp”], /* 抓取图片格式显示 */

/* 上传视频配置 */
“videoActionName”: “uploadvideo”, /* 执行上传视频的action名称 */
“videoFieldName”: “upfile”, /* 提交的视频表单名称 */
“videoPathFormat”: “/ueditor/jsp/upload/video/{yyyy}{mm}{dd}/{time}{rand:6}”, /* 上传保存路径,可以自定义保存路径和文件名格式 */
“videoUrlPrefix”: “”, /* 视频访问路径前缀 */
“videoMaxSize”: 102400000, /* 上传大小限制,单位B,默认100MB */
“videoAllowFiles”: [
“.flv”, “.swf”, “.mkv”, “.avi”, “.rm”, “.rmvb”, “.mpeg”, “.mpg”,
“.ogg”, “.ogv”, “.mov”, “.wmv”, “.mp4”, “.webm”, “.mp3”, “.wav”, “.mid”], /* 上传视频格式显示 */

/* 上传文件配置 */
“fileActionName”: “uploadfile”, /* controller里,执行上传视频的action名称 */
“fileFieldName”: “upfile”, /* 提交的文件表单名称 */
“filePathFormat”: “/ueditor/jsp/upload/file/{yyyy}{mm}{dd}/{time}{rand:6}”, /* 上传保存路径,可以自定义保存路径和文件名格式 */
“fileUrlPrefix”: “”, /* 文件访问路径前缀 */
“fileMaxSize”: 51200000, /* 上传大小限制,单位B,默认50MB */
“fileAllowFiles”: [
“.png”, “.jpg”, “.jpeg”, “.gif”, “.bmp”,
“.flv”, “.swf”, “.mkv”, “.avi”, “.rm”, “.rmvb”, “.mpeg”, “.mpg”,
“.ogg”, “.ogv”, “.mov”, “.wmv”, “.mp4”, “.webm”, “.mp3”, “.wav”, “.mid”,
“.rar”, “.zip”, “.tar”, “.gz”, “.7z”, “.bz2”, “.cab”, “.iso”,
“.doc”, “.docx”, “.xls”, “.xlsx”, “.ppt”, “.pptx”, “.pdf”, “.txt”, “.md”, “.xml”
], /* 上传文件格式显示 */

/* 列出指定目录下的图片 */
“imageManagerActionName”: “listimage”, /* 执行图片管理的action名称 */
“imageManagerListPath”: “/ueditor/jsp/upload/image/”, /* 指定要列出图片的目录 */
“imageManagerListSize”: 20, /* 每次列出文件数量 */
“imageManagerUrlPrefix”: “”, /* 图片访问路径前缀 */
“imageManagerInsertAlign”: “none”, /* 插入的图片浮动方式 */
“imageManagerAllowFiles”: [“.png”, “.jpg”, “.jpeg”, “.gif”, “.bmp”], /* 列出的文件类型 */

/* 列出指定目录下的文件 */
“fileManagerActionName”: “listfile”, /* 执行文件管理的action名称 */
“fileManagerListPath”: “/ueditor/jsp/upload/file/”, /* 指定要列出文件的目录 */
“fileManagerUrlPrefix”: “”, /* 文件访问路径前缀 */
“fileManagerListSize”: 20, /* 每次列出文件数量 */
“fileManagerAllowFiles”: [
“.png”, “.jpg”, “.jpeg”, “.gif”, “.bmp”,
“.flv”, “.swf”, “.mkv”, “.avi”, “.rm”, “.rmvb”, “.mpeg”, “.mpg”,
“.ogg”, “.ogv”, “.mov”, “.wmv”, “.mp4”, “.webm”, “.mp3”, “.wav”, “.mid”,
“.rar”, “.zip”, “.tar”, “.gz”, “.7z”, “.bz2”, “.cab”, “.iso”,
“.doc”, “.docx”, “.xls”, “.xlsx”, “.ppt”, “.pptx”, “.pdf”, “.txt”, “.md”, “.xml”
] /* 列出的文件类型 */

 

前端上传图片到服务器

前端上传代码:

<!DOCTYPE html>
<html>
<head>
<meta charset=”UTF-8″>
<title>文件上传</title>
</head>
<body>
<form action=”upload.do” method=”post” enctype=”multipart/form-data”>
<input name=”file” type=”file”>
<input type=”submit” value=”上传文件”>
</form>
</body>
</html>
服务端代码:

package cn.te.spring.controller;

import java.io.File;
import java.io.IOException;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

@Controller
public class UploadController {
@RequestMapping(“/upload.do”)
public String handleUpload(HttpServletRequest request,
@RequestParam(“file”)CommonsMultipartFile file) throws IllegalStateException, IOException {
//CommonsMultipartFile是springmvc封装的上传数据
//确定文件名
String fileName=”upFile.exe”;
//获取文件夹的真实路径
String folderPath=request.getServletContext().getRealPath(“upload”);
//确定文件夹,必须是webapp下面的某个文件夹,否则用户访问不到
File parentFile=new File(folderPath);
//确定上传的文件存储到的目标文件
File dest=new File(parentFile,fileName);
//将文件上传的数据进行存储
file.transferTo(dest);
return null;
}
}

两种方式上传图片文件到服务器(包含前后端代码)

前言:
在 Web 应用中,上传图片功能很常见,今天就给大家分享两种上传图片到服务器的方法,包含前后端代码(后台用 Node)。
本文使用的环境为:

express 版本为 4.17.1
fs 版本为 7.0.1
path 版本为 0.1.7
multer 版本为 1.4.1
uuid 版本为 3.3.2
formidable 版本为 1.2.1
一、使用 form 表单
1. 前端代码

<html lang=”en”>
<head>
<meta charset=”utf-8″/>
<title>文件上传表单</title>
</head>
<body>
<h3>文件上传:</h3>
选择一个文件上传: <br />
<form action=”/file_upload” method=”post” enctype=”multipart/form-data”>
<input type=”file” name=”image” size=”50″ />
<br />
<button type=”submit”>上传</button>
</form>
</body>
</html>

注意点:

请求方式为 post
需要设置 form 表单的 enctype 属性为 “multipart/form-data”
2. 后端代码

var express = require(‘express’);
var app = express();
var fs = require(“fs”);
var multer = require(‘multer’);
app.use(‘/public’,express.static(‘public’));
app.use(multer({ dest: ‘/tmp/’}).array(‘image’));
app.post(‘/file_upload’,function (req, res) {
fs.readFile( req.files[0].path, function (err, data) {
var des_file = __dirname + “/public/img/” + req.files[0].originalname;
fs.writeFile(des_file, data, function (err) {
if(err){
console.log( err );
}else{
var response = {
message:’文件上传成功’,
filename:req.files[0].originalname
};
}
res.send( JSON.stringify( response ) );
});
});
})
var server = app.listen(8081, function () {
var host = server.address().address;
var port = server.address().port;
console.log(“应用实例,访问地址为 http://”, host, port)
})

注意点:

除了常规的 express 模块和 fs 模块,还需要用到 multer 模块
二、使用 input 监听 change 事件
1. 前端代码

<html lang=”en”>
<head>
<meta charset=”utf-8″/>
<title>文件上传表单</title>
</head>
<body>
<h3>文件上传:</h3>
选择一个文件上传: <br />
<input id=”file” type=”file” name=”image” size=”50″ />
<!–<img src=”” alt=”” id=”img”>–>
</body>
<script src=”jquery.min.js”></script>
<script>
$(‘#file’).change(function(){
var input = $(‘#file’)[0];
//图片上传成功后会将图片名称赋值给 value 属性
if(input.value){
//使用 FormData 对象
var formData = new FormData();
//将图片对象添加到 files
formData.append(‘files’, $(‘#file’)[0].files[0])
//使用 ajax 上传图片
$.ajax({
url: ‘http://localhost:8081/file_upload’,
type: ‘POST’,
cache: false,
data: formData,
processData: false,
contentType: false
}).done(function(res) {
console.log(res);
}).fail(function(res) {
console.log(res);
});
//以下是将图片显示到 img 标签上
// var pic = input.files[0];
// var read = new FileReader();
// read.onload = function (e) {
// document.getElementById(‘img’).src = e.target.result;
// };
// read.readAsDataURL(pic);
}
})
</script>
</html>

注意点:

需要使用 FormData 对象
processData 和 contentType 均为 false
2. 后端代码

var express = require(‘express’);
var app = express();
var fs = require(“fs”);
var path = require(‘path’);
var uuid = require(‘uuid’);
var formidable = require(‘formidable’);
app.use(‘/public’,express.static(‘public’));
app.post(‘/file_upload’,function (req, res) {
//创建表单
var form = new formidable.IncomingForm();
//设置编码格式
form.encoding = ‘utf-8’;
//设置文件存储路径
form.uploadDir = “public/img”;
//保留后缀
form.keepExtensions = true;
//设置单文件大小限制 2m
form.maxFieldsSize = 2 * 1024 * 1024;
//form.maxFields = 1000; 设置所以文件的大小总和
form.parse(req, function (err, fields, files) {
var file = files.files;
let picName = uuid.v1() + path.extname(file.name);
fs.rename(file.path, ‘public\\img\\’ + picName, function (err) {
if (err) return res.send({ “error”: 403, “message”: “图片保存异常!” });
res.send({ “picAddr”: picName });
});
});
})
var server = app.listen(8081, function () {
var host = server.address().address;
var port = server.address().port;
console.log(“应用实例,访问地址为 http://”, host, port)
})

注意点:

除了常规的 express 模块、fs 模块 和 path 模块,还需要用到 uuid 模块与 formidable 模块

java大文件(百M以上)的上传下载功能

javaweb上传文件

上传文件的jsp中的部分

上传文件同样可以使用form表单向后端发请求,也可以使用 ajax向后端发请求

1.通过form表单向后端发送请求

<form id=”postForm” action=”${pageContext.request.contextPath}/UploadServlet” method=”post” enctype=”multipart/form-data”>

<div class=”bbxx wrap”>

<inputtype=”text” id=”side-profile-name” name=”username” class=”form-control”>

<inputtype=”file” id=”example-file-input” name=”avatar”>

<button type=”submit” class=”btn btn-effect-ripple btn-primary”>Save</button>

</div>

</form>

改进后的代码不需要form标签,直接由控件来实现。开发人员只需要关注业务逻辑即可。JS中已经帮我们封闭好了

this.post_file = function ()

{

$.each(this.ui.btn, function (i, n) { n.hide();});

this.ui.btn.stop.show();

this.State = this.Config.state.Posting;//

this.app.postFile({ id: this.fileSvr.id, pathLoc: this.fileSvr.pathLoc, pathSvr:this.fileSvr.pathSvr,lenSvr: this.fileSvr.lenSvr, fields: this.fields });

};

通过监控工具可以看到控件提交的数据,非常的清晰,调试也非常的简单。

2.通过ajax向后端发送请求

$.ajax({

url : “${pageContext.request.contextPath}/UploadServlet”,

type : “POST”,

data : $( ‘#postForm’).serialize(),

success : function(data) {

$( ‘#serverResponse’).html(data);

},

error : function(data) {

$( ‘#serverResponse’).html(data.status + ” : ” + data.statusText + ” : ” + data.responseText);

}

});

ajax分为两部分,一部分是初始化,文件在上传前通过AJAX请求通知服务端进行初始化操作

this.md5_complete = function (json)

{

this.fileSvr.md5 = json.md5;

this.ui.msg.text(“MD5计算完毕,开始连接服务器…”);

this.event.md5Complete(this, json.md5);//biz event

 

var loc_path = encodeURIComponent(this.fileSvr.pathLoc);

var loc_len = this.fileSvr.lenLoc;

var loc_size = this.fileSvr.sizeLoc;

var param = jQuery.extend({}, this.fields, this.Config.bizData, { md5: json.md5, id: this.fileSvr.id, lenLoc: loc_len, sizeLoc: loc_size, pathLoc: loc_path, time: new Date().getTime() });

 

$.ajax({

type: “GET”

, dataType: ‘jsonp’

, jsonp: “callback” //自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名

, url: this.Config[“UrlCreate”]

, data: param

, success: function (sv)

{

_this.svr_create(sv);

}

, error: function (req, txt, err)

{

_this.Manager.RemoveQueuePost(_this.fileSvr.id);

alert(“向服务器发送MD5信息错误!” + req.responseText);

_this.ui.msg.text(“向服务器发送MD5信息错误”);

_this.ui.btn.cancel.show();

_this.ui.btn.stop.hide();

}

, complete: function (req, sta) { req = null; }

});

};

 

在文件上传完后向服务器发送通知

this.post_complete = function (json)

{

this.fileSvr.perSvr = “100%”;

this.fileSvr.complete = true;

$.each(this.ui.btn, function (i, n)

{

n.hide();

});

this.ui.process.css(“width”, “100%”);

this.ui.percent.text(“(100%)”);

this.ui.msg.text(“上传完成”);

this.Manager.arrFilesComplete.push(this);

this.State = this.Config.state.Complete;

//从上传列表中删除

this.Manager.RemoveQueuePost(this.fileSvr.id);

//从未上传列表中删除

this.Manager.RemoveQueueWait(this.fileSvr.id);

 

var param = { md5: this.fileSvr.md5, uid: this.uid, id: this.fileSvr.id, time: new Date().getTime() };

 

$.ajax({

type: “GET”

, dataType: ‘jsonp’

, jsonp: “callback” //自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名

, url: _this.Config[“UrlComplete”]

, data: param

, success: function (msg)

{

_this.event.fileComplete(_this);//触发事件

_this.post_next();

}

, error: function (req, txt, err) { alert(“文件-向服务器发送Complete信息错误!” + req.responseText); }

, complete: function (req, sta) { req = null; }

});

};

 

这里需要处理一个MD5秒传的逻辑,当服务器存在相同文件时,不需要用户再上传,而是直接通知用户秒传

this.post_complete_quick = function ()

{

this.fileSvr.perSvr = “100%”;

this.fileSvr.complete = true;

this.ui.btn.stop.hide();

this.ui.process.css(“width”, “100%”);

this.ui.percent.text(“(100%)”);

this.ui.msg.text(“服务器存在相同文件,快速上传成功。”);

this.Manager.arrFilesComplete.push(this);

this.State = this.Config.state.Complete;

//从上传列表中删除

this.Manager.RemoveQueuePost(this.fileSvr.id);

//从未上传列表中删除

this.Manager.RemoveQueueWait(this.fileSvr.id);

//添加到文件列表

this.post_next();

this.event.fileComplete(this);//触发事件

};

这里可以看到秒传的逻辑是非常 简单的,并不是特别的复杂。

var form = new FormData();

form.append(“username”,”zxj”);

form.append(“avatar”,file);

//var form = new FormData($(“#postForm”)[0]);

$.ajax({

url:”${pageContext.request.contextPath}/UploadServlet”,

type:”post”,

data:form,

processData:false,

contentType:false,

success:function(data){

 

console.log(data);

}

});

java部分

文件初始化的逻辑,主要代码如下

FileInf fileSvr= new FileInf();

fileSvr.id = id;

fileSvr.fdChild = false;

fileSvr.uid = Integer.parseInt(uid);

fileSvr.nameLoc = PathTool.getName(pathLoc);

fileSvr.pathLoc = pathLoc;

fileSvr.lenLoc = Long.parseLong(lenLoc);

fileSvr.sizeLoc = sizeLoc;

fileSvr.deleted = false;

fileSvr.md5 = md5;

fileSvr.nameSvr = fileSvr.nameLoc;

 

//所有单个文件均以uuid/file方式存储

PathBuilderUuid pb = new PathBuilderUuid();

fileSvr.pathSvr = pb.genFile(fileSvr.uid,fileSvr);

fileSvr.pathSvr = fileSvr.pathSvr.replace(“\\”,”/”);

 

DBConfig cfg = new DBConfig();

DBFile db = cfg.db();

FileInf fileExist = new FileInf();

 

boolean exist = db.exist_file(md5,fileExist);

//数据库已存在相同文件,且有上传进度,则直接使用此信息

if(exist && fileExist.lenSvr > 1)

{

fileSvr.nameSvr             = fileExist.nameSvr;

fileSvr.pathSvr        = fileExist.pathSvr;

fileSvr.perSvr              = fileExist.perSvr;

fileSvr.lenSvr              = fileExist.lenSvr;

fileSvr.complete       = fileExist.complete;

db.Add(fileSvr);

 

//触发事件

up6_biz_event.file_create_same(fileSvr);

}//此文件不存在

else

{

db.Add(fileSvr);

//触发事件

up6_biz_event.file_create(fileSvr);

 

FileBlockWriter fr = new FileBlockWriter();

fr.CreateFile(fileSvr.pathSvr,fileSvr.lenLoc);

}

接收文件块数据,在这个逻辑中我们接收文件块数据。控件对数据进行了优化,可以方便调试。如果用监控工具可以看到控件提交的数据。

boolean isMultipart = ServletFileUpload.isMultipartContent(request);

FileItemFactory factory = new DiskFileItemFactory();

ServletFileUpload upload = new ServletFileUpload(factory);

List files = null;

try

{

files = upload.parseRequest(request);

}

catch (FileUploadException e)

{// 解析文件数据错误

out.println(“read file data error:” + e.toString());

return;

 

}

 

FileItem rangeFile = null;

// 得到所有上传的文件

Iterator fileItr = files.iterator();

// 循环处理所有文件

while (fileItr.hasNext())

{

// 得到当前文件

rangeFile = (FileItem) fileItr.next();

if(StringUtils.equals( rangeFile.getFieldName(),”pathSvr”))

{

pathSvr = rangeFile.getString();

pathSvr = PathTool.url_decode(pathSvr);

}

}

 

boolean verify = false;

String msg = “”;

String md5Svr = “”;

long blockSizeSvr = rangeFile.getSize();

if(!StringUtils.isBlank(blockMd5))

{

md5Svr = Md5Tool.fileToMD5(rangeFile.getInputStream());

}

 

verify = Integer.parseInt(blockSize) == blockSizeSvr;

if(!verify)

{

msg = “block size error sizeSvr:” + blockSizeSvr + “sizeLoc:” + blockSize;

}

 

if(verify && !StringUtils.isBlank(blockMd5))

{

verify = md5Svr.equals(blockMd5);

if(!verify) msg = “block md5 error”;

}

 

if(verify)

{

//保存文件块数据

FileBlockWriter res = new FileBlockWriter();

//仅*块创建

if( Integer.parseInt(blockIndex)==1) res.CreateFile(pathSvr,Long.parseLong(lenLoc));

res.write( Long.parseLong(blockOffset),pathSvr,rangeFile);

up6_biz_event.file_post_block(id,Integer.parseInt(blockIndex));

 

JSONObject o = new JSONObject();

o.put(“msg”, “ok”);

o.put(“md5”, md5Svr);

o.put(“offset”, blockOffset);//基于文件的块偏移位置

msg = o.toString();

}

rangeFile.delete();

out.write(msg);

 

注:

1. 上面的java部分的代码可以直接使用,只需要将上传的图片路径及收集数据并将数据写入到数据库即可

2. 上面上传文件使用到了字节流,其实还可以使用别的流,这个需要读者自己在下面完善测试

3. BeanUtils是一个工具 便于将实体对应的属性赋给实体

4. 上传文件不能使用 request.getParameter(“”)获取参数了,而是直接将request解析,通过判断每一项是文件还是非文件,然后进行相应的操作(文件的话就是用流来读取,非文件的话,暂时保存到一个map中。)

前端图片文件上传到服务器

以前ajax不能上传图片 现在有了formdata可以直接ajax上传

代码:

<form action=”DinoVoteHelper.ashx?action=upload” enctype=”multipart/form-data” method=”post” id=”form” target=”if”>
<input style=”display: none” id=”sfzzm” type=”file” name=”file1″ onchange=”javascript:showpic(this,’sfzzmImg’);” accept=”image/*”>
</form>
<iframe id=”if” name=”if” style=”display:none”></iframe>
js:

//上传数据
var formData = new FormData($(“#form”)[0]);
$.ajax({
url: ‘DinoVoteHelper.ashx?action=upload’,
type: ‘POST’,
data: formData,
async: false,
cache: false,
contentType: false,
processData: false,
success: function (returndata) {
debugger
alert(returndata);
},
error: function (returndata) {
alert(returndata);
}
});
后台:

if (context.Request.Files.Count > 0)
{
string filename = context.Request.Files[0].FileName;//获取文件名
string[] temp = filename.Split(‘.’);//获取文件后缀
string path = “photo/DinoVote/” + my_model.id + “_” + DateTime.Now.ToString(“MMddHHmmss”) +”.”+ temp[1];
string strPath = Path.Combine(basePath, path);
context.Request.Files[0].SaveAs(strPath);

hd_articlevote_images img = new hd_articlevote_images()
{
hd_code = DinoVoteManage.hd_code,
images = path,
article_code = my_model.id.ToString()
};
main_db.hd_articlevote_images.Add(img);
main_db.SaveChanges();
}
图片转byte[]:

 

string str = @”D:\1.png”;
//读文件
FileStream fs = new FileStream(str, FileMode.Open, FileAccess.Read);
BinaryReader by = new BinaryReader(fs);
int length = (int)fs.Length;
//转成byte[]
byte[] imgbyte = by.ReadBytes(length);

把byte[]保存为图片类型(如果byte[]不正确则可能保存报错)

//转成IO流
MemoryStream ms = new MemoryStream(imgbyte);
ms.Seek(0, SeekOrigin.Begin);
//通过流生成图片
Image image1 = Image.FromStream(ms);
//保存
image1.Save(@”D:\test.png”);
图片上传到服务器(App POST传到后台):

 

string fPath = context.Server.MapPath(“severphotes”); //服务器路径
#region 上传头像
if (!Directory.Exists(fPath)) //判断是否存在文件夹
{
Directory.CreateDirectory(fPath);
}

TimeSpan ts = DateTime.UtcNow – new DateTime(1970, 1, 1, 0, 0, 0, 0); //时间戳
string name = Convert.ToInt64(ts.TotalSeconds).ToString() + “.png”; //图片名
//if (!File.Exists(fPath + name))
//{
System.IO.Stream stream = context.Request.InputStream; //得到传来的流
byte[] buffer = new byte[stream.Length];
FileStream fs = null;
try
{
fs = new FileStream(fPath + “//” + name, FileMode.Create); //按照路径创建图片文件
while ((stream.Read(buffer, 0, buffer.Length)) > 0)
{
fs.Write(buffer, 0, buffer.Length); //写入数据
}
}
catch (IOException ioe)
{
context.Response.Write(ioe);
}
finally
{
if (fs != null)
{
fs.Close();
}
stream.Close();
}
Form表单提交(input控件添加图片)保存到服务器

1、

System.Web.HttpFileCollection files = context.Request.Files; //获取FORM表单提交的文件
if (files.Count > 0)
{
System.Web.HttpPostedFile postedfile = files[0];
Stream str = postedfile.InputStream;
StreamReader streamReader = new StreamReader(str);
byte[] bytes = new byte[1024];
//地址名字
TimeSpan ts = DateTime.UtcNow – new DateTime(1970, 1, 1, 0, 0, 0, 0);
string name = Convert.ToInt64(ts.TotalSeconds).ToString() + “.png”;
string fPath = “”;
string url = “”;
string tag = “”;
string imgName = “”;
if (files.AllKeys[0] == “icon”)
{
fPath = context.Request.MapPath(“../../../severphotes”);
url = ConfigurationManager.AppSettings[“website”].ToString() + “severphotes/” + name;
tag = “severphotes/” + name;
imgName = “SF_ICON”;
}
#region 保存图片方法
FileStream fstr = new FileStream(fPath + “//” + name, FileMode.OpenOrCreate, FileAccess.Write);
int len = 0;
while ((len = str.Read(bytes, 0, 1024)) > 0)
{
fstr.Write(bytes, 0, len);
}
streamReader.Dispose();
str.Dispose();
fstr.Flush();
fstr.Close();
#endregion
context.Response.ContentType = “text/html”; //这种方式返回能调用JS
}
2、自带Save:

 

System.Web.HttpFileCollection files = context.Request.Files;
if (files.Count > 0)
{
TimeSpan ts = DateTime.UtcNow – new DateTime(1970, 1, 1, 0, 0, 0, 0);
string name = Convert.ToInt64(ts.TotalSeconds).ToString() + “.png”;
string tag = “image/forward/” + name;
string url = ConfigurationManager.AppSettings[“website”].ToString() + tag;
HttpPostedFile hpFile = files[0];
hpFile.SaveAs(context.Request.MapPath(“../../../image/forward/” + name));
context.Response.ContentType = “text/html”;
context.Response.Write(“<script>parent.setPic(‘” + url + “‘,'” + tag + “‘)</script>”); //在iframe中调用主页面的script需要加parent.
}
HTML部分:

 

function setPic(url, tag) { //给图片赋值路径和tag属性,src是为了显示,tag是在数据库保存的相对路径
$(‘#forwardImg’).attr(“src”, url);
$(‘#forwardImg’).attr(“tag”, tag);
}

function upload() { //图片上传后提交form
$(‘#form’).submit();
}

function uploadImg() { //控件模拟点击,也可以$(“#img”).click();
document.getElementById(‘img’).click();
}
<form target=”filetarget” enctype=”multipart/form-data” method=”post” id=”form” action=”../dzxswebservice/ForwardService.ashx?action=updateImage”>
<div style=”text-align:left;margin-top:40px;”>
<a onclick=”uploadImg()”>
<i class=”icon-shangchuan iconfont”></i>
<input type=”file” onchange=”upload()” name=”img1″ id=”img” style=”display:none”/>上传图片
</a>
</div>
<iframe name=”filetarget” id=”filetarget” style=”display: none”></iframe>
</form>
样式什么的可以不看,主要的是form、input、iframe、a标签

 

这里需要给form添加 enctype=”multipart/form-data” 否则无法上传

这里*次点击触发的是uploadImg方法,input就会显示选择文件,确认选择后则自动触发onchange事件提交。在后台可以限制文件大小。

 

上传文件到服务器例子,包括前端后台一整套

首先从前端开始:
uploadFile.jsp
<%@ page language=”java” contentType=”text/html; charset=UTF-8″
pageEncoding=”UTF-8″%>
<!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”>
<html>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″>
<title>Insert title here</title>
</head>
<script src=”js/jquery-1.12.0.min.js”></script>
<script src=”js/jquery.form.js”></script>
<body>
<form id=”upfile”>
选择一个文件:
<input type=”file” name=”file” id=”upload” />
<br/><br/>
<input id=”uploadFile” value=”上传” type=”button”/>
</form>

<div id=”upFile”></div>
</body>
<script type=”text/javascript”>

$(“#uploadFile”).click(function(){
var formData = new FormData($(“#upfile”)[0]);
//formData.set(‘file’, document.getElementById(“upload”).files[0]);
$.ajax({
url: ‘${pageContext.request.contextPath}/uploadFile/upload’,
type: ‘POST’,
cache: false,
data: formData,
processData: false,
contentType: false
}).done(function(res) {

});
});
</script>
</html>
上面那个js中注掉的那行,是 直接定位到file那个标签,但是set方法在低版本的浏览器上不支持,会报错只供参考,注意file标签必须是在form里面,记得不能嵌套form,只能有一个form;一般form都是<form method=”post” action=”” enctype=”multipart/form-data” id=”upfile”></form>;我上面例子只是精简了;

前端完成后到后端:
先配置一些参数:
congif.properties(设置上传文件的大小和服务器地址):
#upload
upload.file.maxsize = 104857600
upload.file.url=http://192.168.102.11/test
pom.xml需要引入的jar包

<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-webdav-jackrabbit</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>slide</groupId>
<artifactId>slide-webdavlib</artifactId>
<version>2.1</version>
</dependency>

spring配置中需要加下面配置:
<!– 引入配置文件中的参数 –>
<context:property-placeholder location=”classpath:config.properties” />
<!– 配置MultipartResolver 用于文件上传 使用spring的CommosMultipartResolver –>
<bean id=”multipartResolver” class=”org.springframework.web.multipart.commons.CommonsMultipartResolver”>
<property name=”defaultEncoding” value=”UTF-8″></property>
<property name=”maxUploadSize” value=”50485760″></property>
<property name=”resolveLazily” value=”true”></property>
</bean>

<!– 对传入的url进行一些处理,这个<span style=”font-family: Arial, Helvetica, sans-serif;”>UploadFileUtils类所在路径需要根据你实际放置到项目的路径一致,这个类我下面提供了,建完这个类后别忘记把class中的路径替换掉</span>–>
<bean class=”com.yoho.common.UploadFileUtils”>
<property name=”downloadUrl” value=”${upload.file.url}”/>
</bean>
UploadFileUtils.java(这个类主要是对config中的url参数的一些处理)
import org.apache.commons.lang3.StringUtils;

public class UploadFileUtils {

public static String downloadUrl = null;

/**
* *对路径与相对路径转换
*/
public static String addPrefix(String url) {
// 本身就是*对路径直接返回
if (url == null || url.startsWith(“http://”) || url.startsWith(“https://”)) {
return url;
}
return URLUtils.append(downloadUrl, url);
}

public static String removePrefix(String url) {
// 如果 url 不符合条件,直接返回
if (url == null || !url.startsWith(downloadUrl)) {
return url;
}
return StringUtils.removeStart(url, downloadUrl);
}

public void setDownloadUrl(String downloadUrl) {
UploadFileUtils.downloadUrl = downloadUrl;
}
}
controller接收请求:
UploadFile.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.yoho.model.FileInfo;
import com.yoho.service.UploadService;

/**
* @author zelei.fan
*
*/
@Controller
@RequestMapping(“/uploadFile”)
public class UploadFile{

@Autowired
private UploadService uploadService;
/**
* 需要在spring-mvc中配置MultipartFile相关信息,以及引进fileUpload的依赖
* @param multipartFile
* @return
* @throws Exception
*/
@RequestMapping(value = “/upload”, method = RequestMethod.POST)
@ResponseBody
public FileInfo upload(@RequestParam(“file”)MultipartFile multipartFile)throws Exception{

FileInfo fileInfo = uploadService.uploadFile(multipartFile);
return fileInfo;
}

}
实体类,封装了一些属性,文件名,文件类型,相对地址,*对地址
FileInfo.java
public class FileInfo {

private String fileName;

private String fileType;

private String absoluteUrl;

private String relativeUrl;

private String fileSize;

private String fileMd5;

private String createAt;

public String getFileName() {
return fileName;
}

public void setFileName(String fileName) {
this.fileName = fileName;
}

public String getFileType() {
return fileType;
}

public void setFileType(String fileType) {
this.fileType = fileType;
}

public String getAbsoluteUrl() {
return absoluteUrl;
}

public void setAbsoluteUrl(String absoluteUrl) {
this.absoluteUrl = absoluteUrl;
}

public String getRelativeUrl() {
return relativeUrl;
}

public void setRelativeUrl(String relativeUrl) {
this.relativeUrl = relativeUrl;
}

public String getFileSize() {
return fileSize;
}

public void setFileSize(String fileSize) {
this.fileSize = fileSize;
}

public String getFileMd5() {
return fileMd5;
}

public void setFileMd5(String fileMd5) {
this.fileMd5 = fileMd5;
}

public String getCreateAt() {
return createAt;
}

public void setCreateAt(String createAt) {
this.createAt = createAt;
}

}

然后到service中进行业务处理:
UploadService.java(业务处理接口)
import org.springframework.web.multipart.MultipartFile;

import com.yoho.model.FileInfo;

public interface UploadService {

/**
* 上传文件,返回链接
* @param multipartFile
* @return
*/
FileInfo uploadFile(MultipartFile multipartFile);

}

具体的实现类:
UploadServiceImpl.java
import static com.google.common.base.Preconditions.checkArgument;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.commons.httpclient.HttpURL;
import org.apache.webdav.lib.WebdavResource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.PeekingIterator;
import com.yoho.common.DateUtil;
import com.yoho.common.MD5;
import com.yoho.model.FileInfo;
import com.yoho.service.UploadService;

/**
*
* @author zelei.fan
*
*/
@Service
public class UploadServiceImpl implements UploadService{

@Value(“${upload.file.url}”)
private String uploadUrl;

@Value(“webdav”)
private String uploadUsername;

@Value(“webdav”)
public String uploadPassword;

@Override
public FileInfo uploadFile(MultipartFile multipartFile) {

//取到文件大小,如果超过指定范围的话就直接返回提醒错误
long size = multipartFile.getSize();
//获取文件名
String fileName = multipartFile.getOriginalFilename();
// 获取文件后缀,即文件类型
String fileExt = “”;
if (fileName.contains(“.”)) {
fileExt = fileName.substring(fileName.lastIndexOf(“.”) + 1).toLowerCase();
}
//设置MD5加密
String fileMD5 = md5File(multipartFile);

//拼接文件路径:/后缀/年/月/日/md5/filename
String saveUrl = “/” + fileExt + new SimpleDateFormat(“/yyyy/MM/dd/”).format(new Date()) + fileMD5 + “/” + multipartFile.getOriginalFilename();

String location = null;
try {
location = saveFile(multipartFile, saveUrl);//保存文件操作
} catch (Exception e) {
e.printStackTrace();
}

FileInfo fileInfo = new FileInfo();
fileInfo.setAbsoluteUrl(location);
fileInfo.setRelativeUrl(saveUrl);
fileInfo.setFileMd5(fileMD5);
fileInfo.setFileName(fileName);
fileInfo.setFileSize(String.valueOf(size));
fileInfo.setFileType(fileExt);
fileInfo.setCreateAt(DateUtil.date2String(new Date(), DateUtil.DATE_TIME_FORMAT));
return fileInfo;

}

//MD5加密
private String md5File(MultipartFile multipartFile) {
try {
return MD5.md5(multipartFile.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

private String saveFile(MultipartFile file, String savePath) throws Exception {
// 上传文件,到文件服务器,uploadUrl是在config中配好的,就是给uploadUrl赋值,如果不用那么麻烦的话可以直接把url放进去:upload(“http://192.168.102.11/test”, uploadUsername, uploadPassword, savePath, file.getBytes());
upload(uploadUrl, uploadUsername/*那台服务器的用户名*/, uploadPassword/*那台服务器的密码*/, savePath, file.getBytes());
return append(uploadUrl, savePath);

}

public static void upload(String webDavServer, String webDavUser, String webDavPassword, String remotePath, byte[] bytes) throws IOException {

if (!webDavServer.endsWith(“/”)) webDavServer += “/”;

//连接服务器
HttpURL hrl = new HttpURL(webDavServer);
hrl.setUserinfo(webDavUser, webDavPassword);

WebdavResource wdr = new WebdavResource(hrl);

//make directory if need
StringBuffer ssdir = new StringBuffer();
// 去除*后一个文件名
StringTokenizer t = new StringTokenizer(remotePath.substring(0, remotePath.lastIndexOf(“/”)), “/”);
while(t.hasMoreTokens()){
String sdir = t.nextToken();
ssdir.append(sdir+”/”);
wdr.mkcolMethod(wdr.getPath() + ssdir );
}

String remoteFile= wdr.getPath() + remotePath;//拼成*对地址
boolean result = wdr.putMethod(remoteFile, bytes);//把文件写进去
checkArgument(result, “文件上传出错”);//false时会报错,true则为成功

wdr.close();//*后关闭连接

}

/**
* 连接 URL
* @param paths
* @return
*/
public static String append(String… paths) {
List<String> pathList = Lists.newArrayList(paths);
PeekingIterator<String> iter = Iterators.peekingIterator(pathList.iterator());
StringBuilder urlBuilder = new StringBuilder();
while (iter.hasNext()) {
String current = iter.next();
urlBuilder.append(current);
if (!iter.hasNext()) {
break;
}
if (current.endsWith(“/”) && iter.peek().startsWith(“/”)) {
urlBuilder.deleteCharAt(urlBuilder.length() – 1);
} else if (!current.endsWith(“/”) && !iter.peek().startsWith(“/”)) {
urlBuilder.append(“/”);
}
}
return urlBuilder.toString();
}

}
所有的代码都已经在上面了,我已经测试过能上传成功,相应方法的注释页标在上面,希望你能运行成功;如果有什么问题可以留言给我,尽量能帮到你

前端上传文件到服务器端

上传文件到服务器有很多插件,而且各种UI组件库,像iview,eleUI也都替我们封装好了,根本不用我们操心。
下面是原生的form 上传
原生form表单上传文件
直接上代码

<form enctype=’multipart/form-data’ action=”http://192.168。xxx.xxx:3000/upload” method=”post”>
<input type=”file” name=’pic’>
<input type=”submit”>
</form >

首先我们看form的enctype 属性
enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码。默认地,表单数据会编码为 “application/x-www-form-urlencoded”。就是说,在发送到服务器之前,所有字符都会进行编码,但是在这里上传文件,我们用multipart/form-data,通过二进制方式提交。

action是你上传文件的地址路径,method是以什么方式进行处理,post为上传,get为获取。
前端很简单,重要的是后端,博主用node起了一个服务器,废话不说,上代码

var multiparty = require(‘multiparty’)

router.post(‘/upload’,function (req,res) {
//创建处理form表单的对象
var form = new multiparty.Form({
uploadDir: ‘./images’
})//文件上传的路径
//通过form.parse生成服务器文件,注意这里生成的文件名字不是原来的名字,而是编码后的名字,里面的回调包含错误信息,字段信息,文件信息
form.parse(req,function (err,fields,files) {
if(err) {throw err ;
console.log(err)}
else{
res.send(JSON.stringify(files.pic[0]))
}
})
})

这里需要安装插件multiparty
执行命令npm i multiparty
作用:使用内容类型解析http请求multipart/form-data,也称为文件上载。

HTTP流媒体播放技术发展以及nginx点播源站

互联网多媒体内容传输从大方向上可以分为下载传输和流式传输,而流式传输又可以分为顺序流式传输和实时流式传输,换句话来说就是点播(Video on Demand)和直播(Live Streaming),顾名思义,前者的媒体内容是提前存储在服务器上供客户端请求播放,而后者是实时产生并分发给客户端播放。

本文主要是基于HTTP的流媒体点播技术入门,讲述了一些基本的概念以及这一技术的变革演进。

互联网上传播音视频内容*早从上世纪90年代开始,RTP等专有协议的提出就是定义音视频的包格式以获得更低的网络传递开销。不过现如今的互联网世界,CDN扮演着非常重要的角色,而*大部分CDN厂商是不支持RTP等协议的。基于HTTP的流媒体点播技术则占据着主流,其有几点优势:1、防火墙友好;2、客户端控制媒体流的访问,服务端不需要为每一个客户端连接维护媒体session状态;3、采用标准的HTTP服务器即可,支撑大规模客户端访问不需要额外的服务器技术资源开销,*重要的就是CDN技术的良好支持优化。

HTTP流媒体技术的变革大致经过了以下四个阶段:

 

*初,我们只能先下载完整视频文件到本地磁盘后,然后才能播放观赏。这意味着你必须等待视频下载操作的完成,严格来讲,根据我们*段的划分,这不能称作为流媒体传输,不过我们还是列出来,以作对比。

 

在上面简单下载文件的基础上,一个明显的提升就是渐进式下载技术。这种情况下不需要完整下载视频文件,可以边下载边播放,这就要求视频的元数据信息得放在视频文件的开头。但是,我们只能在已经下载的那一部分自由拖拽播放,而未下载的部分是不能播放的。

 

在渐进式下载的基础上,pseudo-streaming(伪流)出现了,其增强了seek播放功能,也就是支持直接切到未下载的地方进行观看,所以也可以称之为渐进式播放。

 

其实,到pseudo-streaming 这一步,流媒体播放的技术已经很成熟了,而且目前*大多数的视频网站都是这种技术,比如Youtube、优酷、腾讯视频等。如果,要在这个基础上再一步的优化,可以从哪些方面呢?比较容易想到的就是带宽优化,能够链路感知,这就是下面的自适应比特率流。其原理就是,同一视频内容会有不同的码率版本,客户端播放器会动态地根据网络质量切换请求带宽匹配的的视频片段。

 

自适应码率流媒体技术,业内比较常见的实现是Apple的HTTP Live Streaming (HLS)以及Adobe的HTTP Dynamic Streaming (HDS),下图简单展示了HLS的文件结构,可以看到master playlist包含了两种不同码率的播放列表,当然如果你只有一种版本,那么就不需要master playlist了。基于HTTP的自适应码率流媒体是有国际标准的,那就是3GPP组织和MPEG小组所提出的MPEG-DASH(Dynamic Adaptive Streaming over HTTP)。

 

*后,我们来说说一个简单的点播流媒体服务器是怎么工作的。

目前,很常见的一种搭建方式就是基于nginx,而nginx本身对音视频媒体的处理就有一定的支持,官方就有flv和mp4的插件,即ngx_http_flv_module和ngx_http_mp4_module。前者支持以字节偏移的渐进式seek播放,后者支持以时间偏移的渐进式seek播放。

要想达到这种渐进式播放的目的,大部分情况下,我们是要对服务端的媒体文件进行一定的处理的。首先播放之前,服务端需要先返回一定的视频元数据信息,其次我们需要知道媒体时间和媒体数据的映射关系。为什么呢?

目前大部分的视频编码都采用H264标准,而H264的不同视频帧采用了不同的压缩编码方式,其中I帧的作用很大,其通常是每个 GOP(Group of Picture)的*个帧,我们简单点说,I帧依靠自身就可以解码出完整图像,而其后面帧的解码则需要依赖I帧,这意味着什么?

是的,这决定了我们在拖拽播放的时候,播放器会seek到*近的I帧处,所以有时候会有一个现象,就是你拖到一个精确的时间点,但是播放器却在另一个靠近的时间点开始播放,这就是因为那个时间点是I帧所在之处。

播放器的seek播放通常是让用户选择时间偏移,而服务端*终对文件的请求处理只能是字节偏移,所以问题来了,我们要有一个时间偏移和字节偏移的映射表,严格一点说,是每一个I帧的时间偏移与字节偏移映射。

问题又来了,这个映射是放在客户端还是服务端处理呢?当然是都可以。如果客户端发出字节偏移请求,那么服务端就很轻松,只需要提供HTTP range访问的功能,但是client端需要提前知道时间与字节的映射关系,比如flv格式的metadata就提供了这个信息,有的flv文件没有加入metadata,这时候就需要用相应的flv工具(比如flvmeta)去处理成这样的格式,这样客户端的播放器就可以直接发送字节偏移量了。MP4则不需要这么处理,因为MP4格式本身就要求带这样的信息,其记录metadata的Movie Box (moov)中的Media Information Box (minf)就存储了媒体时间和媒体数据的映射关系。如果客户端只发出时间请求,那么服务端就得将时间偏移请求转换为字节偏移请求,然后处理并响应请求。

上面讲了这么多,那请问,在哪里可以买到牛逼的点播服务呢?

网易视频云推出的一站式视频云点播平台,基于分布式处理集群和大规模分发系统资源,满足全终端设备的播放需求,为企业用户提供*速稳定的视频上传、存储、转码、播放和下载等云服务,欢迎试用购买。

nginx搭建基于http协议的视频点播服务器

1,于由自己的服务器上已经安装好nginx(具体安装方法见我的另一篇文章,Linux中安装nginx),所以不再安装。

2,下载nginx_mod_h264_streaming-2.2.7.tar.gz(自己在网上搜吧)。

3,安装pcre,先看有没有安装。

 

[root@localhost vsftpd]# rpm -qa pcre
pcre-7.8-7.el6.i686

已经安装(上篇加rtmp模块的时候已经安装了)

4,解压nginx_mod_h264_streaming-2.2.7.tar.gz,添加flv,mp4模块。解压到和nginx源码同级的目录。然后./configure –add-module=../nginx_mod_h264_streaming-2.2.7 –with-http_flv_module –with-http_mp4_module
因为flv 和http是nginx自带的所以不用下载这个模块。

5,make
可能遇到问题1
/root/nginx_mod_h264_streaming-2.2.7/src/ngx_http_streaming_module.c: In function ‘ngx_streaming_handler’:
/root/nginx_mod_h264_streaming-2.2.7/src/ngx_http_streaming_module.c:158: error: ‘ngx_http_request_t’ has no member named ‘zero_in_uri’
make[1]: *** [objs/addon/src/ngx_http_h264_streaming_module.o] Error 1
make[1]: Leaving directory `/root/nnginx-1.5.9′
make: *** [build] Error 2
那么将src/ngx_http_streaming_module.c文件中以下代码删除或者是注释掉就可以了

6, make install

7,配置 nginx.conf
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {
worker_connections  1024;
}

#include  nginx.conf-rtmp;

http {

include       mime.types;

default_type  application/octet-stream;

log_format main  ‘$remote_addr – $remote_user [$time_local] ‘

‘”$request” $status $bytes_sent ‘

‘”$http_referer” “$http_user_agent” ‘

‘”$gzip_ratio”‘;

keepalive_timeout  60;

server_names_hash_bucket_size  128;

client_header_buffer_size    32k;

 

large_client_header_buffers  4 32k;

 

access_log off;

gzip on;

gzip_min_length  1100;

gzip_buffers     4 8k;

gzip_types       text/plain;

 

output_buffers   1 32k;

postpone_output  1460;

 

client_header_timeout  3m;

client_body_timeout    3m;

send_timeout           3m;

 

sendfile                on;

tcp_nopush              on;

tcp_nodelay             on;

 

######################################################################

 

server {

listen       8999;

server_name  192.168.1.104;

root    /usr/local/nginx/html/flv_file/;

limit_rate_after 5m;
limit_rate 512k;

index   index.html;

charset utf-8;

location ~ \.flv {

flv;

}

location ~ \.mp4$ {
mp4;
}

error_page   500 502 503 504  /50x.html;

location = /50x.html {

root   html;

}
}

}

8,启动nginx,创建 /usr/local/nginx/html/flv_file/目录。

9,上传mp4视频文件CY.mp4。

10,写个html测试。看之前的文章用jwplayer来测试,但是我这边一直报jwplayer udefined。

<!DOCTYPE html>
<div id=”container”>Loading the player …</div>
<video src=”http://192.168.1.104:8999/CY.mp4″ controls=”controls” width=”50%” height=”50%”>
</video>

将nginx作为视频点播服务器

流媒体服务器
流媒体指以流方式在网络中传送音频、视频和多媒体文件的媒体形式。相对于下载后观看的网络播放形式而言,流媒体的典型特征是把连续的音频和视频信息压缩后放到网络服务器上,用户边下载边观看,而不必等待整个文件下载完毕。由于流媒体技术的优越性,该技术广泛应用于视频点播、视频会议、远程教育、远程医疗和在线直播系统中。作为新一代互联网应用的标志,流媒体技术在近几年得到了飞速的发展。

流媒体播放方式
HTTP方式
这种方式要下载FLV视频文件到本地播放,一旦FLV视频文件下载完成,就不会消耗服务器的资源和带宽,但是拖动功能没有RTMP/RTMP流媒体方式强大,很多视频网站都是用HTTP方式实现的,如:YouTube,土豆,酷6等

RTMP/RTMP流媒体方式
这种方式不用下载FLV视频文件到本地,可以实时的播放flv文件,可以任意拖拽播放进度条,但是比较消耗服务器的资源。

安装
nginx 1.1.3之后已经默认支持mp4,flv模块,无须第三方模块支持。我这里使用的是docker容器:

docker run -d –name nginx -p 80:80 -v ~/opt/local/nginx/logs:/var/log/nginx -v ~/opt/local/nginx:/etc/nginx nginx
1
将容器内的nginx配置目录(即/etc/nginx)挂载到了本机目录方便我们对配置文件进行修改。

配置文件
在~/opt/local/nginx/conf.d下新建一个mp4.conf,加入如下配置

1 server {
2 listen 80 ;
3 server_name localhost;
4 root /etc/nginx/mp4;
5 limit_rate 256k;
6
7 location ~ \.flv$ {
8 flv;
9 }
10
11 location /video/ {
12 rewrite ^/video/(.*)$ /jwplayer-7.10.4/$1 last;
13 }
14
15 location ~ \.mp4$ {
16 mp4;
17 # mp4_buffer_size 1m;
18 # mp4_max_buffer_size 5m;
19 limit_rate_after 5m;
20 limit_rate 100k;
21 # limit_conn perip 1;
22 }
23 }

关于nginx的基本配置不再赘述,(可以参考我的博客nginx进阶-配置文件)将由flv与mp4结尾的uri交给对应的模块。随便找一个mp4或flv格式的视频放在root目录下(配置虚拟容器中的路径/etc/nginx/mp4,映射到真实机器的路径时~/opt/local/nginx/mp4)现在我们搭配jwplayer进行视频的点播。

下载jwplayer,我这里使用的7.10.4的免费版本,解压后得到如下目录
%title插图%num

这里注意两个文件,一个是jwplayer.flash.swf,还有一个就是jwplayer.js文件。官方已经贴心的为我们准备好了demo,打卡demo.html进行一些修改:

<script type=”text/javascript” src=”jwplayer.js”></script>
<script>jwplayer.key=”4sNBpozdkb2Gv+IYeWyd+CID9tW2NXdJE5GeSg==”;//修改为自己的key</script>
<div id=”player”>
Loading…
</div>
<script type=”text/javascript”>
jwplayer(“player”).setup({
“flashplayer”: “http://localhost/jwplayer.flash.swf”, //player.swf文件的uri
“file”: “http://localhost/1.mp4”,//视频文件的服务器uri
“aspectratio”: “16:9”,//播放器自适应比例
“height”: “360”,//播放器高度
“type”:”mp4″,//播放文件类型(可选)
“title”: “测试标题”,//标题(可选)
“description”: “测试视频描述”,//描述(可选)
“image”: “http://ww4.sinaimg.cn/large/b6839357jw1e3val80tknj20dw099jsb.jpg”,//视频封面(可选)
“repeat”:”true”,//重复播放(留空则不重复播放)
“autostart”:”true”,//自动播放(留空则不启用自动播放)
});
</script>

重点关注javascript标签中的内容,请配合注释一起食用。

效果
访问我们的nginx http://localhost/video/demo.html

%title插图%num

这样一个简单的视频点播服务器就完成了,其它的需求自行扩展即可。接下来有空会研究一下RTMP流媒体服务器的搭建(懒,逃~~)

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速