工作需求,需要在c#端将文件和数据进行上传,Java端接收文件上传到oss,将数据处理到数据库。
这个教程,秒杀市面百分九十九的帖子,绝对是楼主精心打造!
一、网上的一般做法
网上搜出来的教程,基本都是只上传文件的,但对于如何把数据和文件一起上传,那能找到的资料就很少了,不过不管是单独传文件,还是文件带数据,原理都一样,都是模拟浏览器行为,构造请求头和请求参数上传,对于带文件的,需要指定contentType,然后通过流的方式进行上传。
这种方法,不是很方便,因为既要带文件,又要带数据,尤其如果数据是跟文件相关的时候,不好处理,Java中数据的接收方法如下:
c#中的发送数据如下:
1.完整的c#代码如下:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using NUnit.Framework;
using System.Drawing;
namespace TestProject1
{ /// <summary>
/// 实现文件和参数一起提交
/// </summary>
public static class FormUpload
{
/// <summary>
/// 字符编码格式
/// </summary>
private static readonly Encoding encoding = Encoding.UTF8;
private const string DefaultUserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";
public static String ImageFile2Base64(String imageFile)
{
Image image = Image.FromFile(imageFile);
MemoryStream ms = new MemoryStream();
image.Save(ms, image.RawFormat);
byte[] byteArray = ms.ToArray();
ms.Close();
return Convert.ToBase64String(byteArray);
}
/// <summary>
/// 上传图像和参数
/// </summary>
/// <param name="url">上传地址</param>
/// <param name="image">图像byte数组</param>
/// <param name="imageName">图像名称,带扩展名</param>
/// <param name="data">参数</param>
/// <returns>响应内容</returns>
public static string UploadImageAndData(string url,byte[] image,string imageName,string data)
{
Dictionary<string, object> dic = new Dictionary<string, object>();
//dic.Add("file", new FormUpload.FileParameter(image, imageName));
dic.Add("file", new FormUpload.FileParameter(image, imageName));
//可以封装跟文件相关的其他属性,这里是最舒服的
dic.Add("info", data);
dic.Add("文件相关的业务参数", "1");
try
{
HttpWebResponse r = FormUpload.MultipartFormDataPost(url, DefaultUserAgent, dic);
Stream instream = r.GetResponseStream();
StreamReader sr = new StreamReader(instream, Encoding.UTF8);
//返回结果网页(html)代码
string retValue = sr.ReadToEnd();
r.Close();
sr.Close();
return retValue;
}
catch (Exception e)
{
Debug.WriteLine("文件传输异常: " + e.Message);
throw e;
}
}
public static HttpWebResponse MultipartFormDataPost(string postUrl, string userAgent, Dictionary<string, object> postParameters)
{
//分割标记
string formDataBoundary = String.Format("----------{0:N}", Guid.NewGuid());
//内容类型
string contentType = "multipart/form-data; boundary=" + formDataBoundary;
byte[] formData = GetMultipartFormData(postParameters, formDataBoundary);
return PostForm(postUrl, userAgent, contentType, formData);
}
private static HttpWebResponse PostForm(string postUrl, string userAgent, string contentType, byte[] formData)
{
HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest;
if (request == null)
{
throw new NullReferenceException("request is not a http request");
}
// Set up the request properties.
request.Method = "POST";
request.ContentType = contentType;
//request.ContentType = "application/json;charset=utf-8";
// request.UserAgent = userAgent;
// request.CookieContainer = new CookieContainer();
request.ContentLength = formData.Length;
//request.Timeout = 1000*60;
//request.Headers.Add("Seq","1");
//request.Headers.Add("Authorization", "d10CdKTSbGPqdhrk9HcPvAnknuDp7d2WnsFDrWFA");
//request.Headers.Add("Cha-Code","1501042092");
// You could add authentication here as well if needed:
// request.PreAuthenticate = true;
// request.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;
// request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(System.Text.Encoding.Default.GetBytes("username" + ":" + "password")));
// Send the form data to the request.
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(formData, 0, formData.Length);
requestStream.Close();
}
return request.GetResponse() as HttpWebResponse;
}
private static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary)
{
Stream formDataStream = new System.IO.MemoryStream();
bool needsCLRF = false;
foreach (var param in postParameters)
{
// Thanks to feedback from commenters, add a CRLF to allow multiple parameters to be added.
// Skip it on the first parameter, add it to subsequent parameters.
if (needsCLRF)
{
formDataStream.Write(encoding.GetBytes("\r\n"), 0, encoding.GetByteCount("\r\n"));
}
needsCLRF = true;
if (param.Value is FileParameter)
{
FileParameter fileToUpload = (FileParameter)param.Value;
// Add just the first part of this param, since we will write the file data directly to the Stream
string header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\";\r\nContent-Type: {3}\r\n\r\n", boundary, param.Key, fileToUpload.FileName ?? param.Key, fileToUpload.ContentType ?? "application/octet-stream");
formDataStream.Write(encoding.GetBytes(header), 0, encoding.GetByteCount(header));
// Write the file data directly to the Stream, rather than serializing it to a string.
formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length);
}
else
{
string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}", boundary, param.Key, param.Value);
formDataStream.Write(encoding.GetBytes(postData), 0, encoding.GetByteCount(postData));
}
}
// Add the end of the request. Start with a newline
string footer = "\r\n--" + boundary + "--\r\n";
formDataStream.Write(encoding.GetBytes(footer), 0, encoding.GetByteCount(footer));
// Dump the Stream into a byte[]
formDataStream.Position = 0;
byte[] formData = new byte[formDataStream.Length];
formDataStream.Read(formData, 0, formData.Length); formDataStream.Close();
return formData;
}
/// <summary>
/// 文件参数对象
/// </summary>
public class FileParameter
{
/// <summary>
/// 文件二进制数组
/// </summary>
public byte[] File { get; set; }
/// <summary>
/// 文件名称,带扩展名,例如:aaa.jpg
/// </summary>
public string FileName { get; set; }
/// <summary>
/// 内容类型,默认application/octet-stream
/// </summary>
public string ContentType { get; set; }
public FileParameter(byte[] file)
: this(file, null)
{
}
public FileParameter(byte[] file, string filename)
: this(file, filename, null)
{
}
public FileParameter(byte[] file, string filename, string contenttype)
{
File = file;
FileName = filename;
ContentType = contenttype;
}
}
class Programss
{
[Test]
public void Test1()
{
string retValue = UploadFiles();
Console.WriteLine(retValue);
Console.Read();
}
static string UploadFiles()
{
string url= "http://ip:port/路径";
//string url = "http:.......";
string imageName = Guid.NewGuid().ToString("N") + ".jpg";
byte[] image = File.ReadAllBytes("D:\\1.jpg");
string data = "提交参数";
return FormUpload.UploadImageAndData(url,image, imageName, data);
}
}
}
}
2.Java端接收代码:
//接收实体类
@Data
public class ReportImgVO {
/** 文件*/
private MultipartFile file;
/** 文件名称*/
private String fileName;
private String info;
/** 业务字段*/
private Integer sampleNo;
}
//mvc接收的方法
@RequestMapping("/file")
@ResponseBody
public Map<String,Object> file(ReportImgVO reportImgVO){
Map<String, Object> map =new HashMap<>();
System.out.println(reportImgVO.getFile());
return map;
}
二、更优的选择
上面的方法完全可以实现我的需求,也已经做出来了,但存在的问题是c#端的代码比较复杂,因为文件需要通过流的方式进行上传,后面的一位大神朋友帅翔(坐标上海)的提示下,文件既然是上传后放到oss的,那为何不直接把文件在c#就转换成base64的字符串呢?这样就不需要处理流的问题了。上传就是一个普通的对象。
优化后的方法来了:
using System.Drawing;
//省略中间部分。。。
//文件转base64
public static String ImageFile2Base64(String imageFile)
{
Image image = Image.FromFile(imageFile);
MemoryStream ms = new MemoryStream();
image.Save(ms, image.RawFormat);
byte[] byteArray = ms.ToArray();
ms.Close();
return "data:image/png;base64,"+Convert.ToBase64String(byteArray);
}
注意,转完还不行,需要给这个字符串加上一个头,标识这是一个图片,在这个字符串最前面加上
"data:image/png;base64,",注意,直接拷贝我的代码,逗号不要丢了。
剩余的c#代码就简单了,把跟流相关的都去掉,就是个普通的数据上传,再不需要考虑文件的问题。
Java端的实体类变成:
@Data
public class ReportImgVO extends BaseEntity {
/** 文件,变成了字符串*/
private String file;
/** 文件名称*/
private String fileName;
private String info;
/** 样本号*/
private Integer sampleNo;
}
这样就简单多了,其他没啥太多变化,c#我也不熟,只能看得懂,会改。
三、接收到base64后的下一步处理
接收到的base64字符串直接可以在页面显示的,如图:
在网页的src、url中都可以直接显示成图片。更进一步,我们把图片上传到oss对象存储中。
官方文档:https://help.aliyun.com/document_detail/84781.html?spm=a2c4g.11186623.6.861.4a6513edP5RSuc
核心代码:
搞定,收工!
参考资料:
我的微信公众号:架构真经(关注领取免费资源)
转载:https://blog.csdn.net/m0_37609579/article/details/100561268